contentbase 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp.json +9 -0
- package/CLI.md +593 -0
- package/MCP-DESCRIPTIONS-REVIEW.md +122 -0
- package/MCP-SERVER-SPEC.md +453 -0
- package/PRIMER.md +540 -0
- package/README.md +289 -13
- package/bun.lock +43 -4
- package/dist/cnotes +0 -0
- package/docs/README.md +110 -0
- package/docs/TABLE-OF-CONTENTS.md +7 -0
- package/docs/models.ts +38 -0
- package/models.ts +38 -0
- package/package.json +12 -3
- package/public/web-demo/index.html +813 -0
- package/showcases/node_modules/.cache/.repl_history +3 -0
- package/showcases/vinyl-collection/artists/nina-simone.mdx +1 -1
- package/src/__tests__/semantic-search.integration.test.ts +284 -0
- package/src/api/endpoints/actions.ts +34 -0
- package/src/api/endpoints/doc.ts +187 -0
- package/src/api/endpoints/docs-index.ts +26 -0
- package/src/api/endpoints/document.ts +171 -0
- package/src/api/endpoints/documents.ts +71 -0
- package/src/api/endpoints/inspect.ts +16 -0
- package/src/api/endpoints/models.ts +10 -0
- package/src/api/endpoints/query.ts +88 -0
- package/src/api/endpoints/root.ts +7 -0
- package/src/api/endpoints/search-reindex.ts +65 -0
- package/src/api/endpoints/search-status.ts +55 -0
- package/src/api/endpoints/search.ts +104 -0
- package/src/api/endpoints/semantic-search.ts +120 -0
- package/src/api/endpoints/text-search.ts +63 -0
- package/src/api/endpoints/validate.ts +34 -0
- package/src/api/helpers.ts +97 -0
- package/src/base-model.ts +12 -0
- package/src/cli/commands/action.ts +82 -44
- package/src/cli/commands/console.ts +124 -0
- package/src/cli/commands/create.ts +179 -53
- package/src/cli/commands/embed.ts +323 -0
- package/src/cli/commands/export.ts +58 -24
- package/src/cli/commands/extract.ts +174 -0
- package/src/cli/commands/help.ts +81 -0
- package/src/cli/commands/index.ts +17 -0
- package/src/cli/commands/init.ts +72 -48
- package/src/cli/commands/inspect.ts +56 -46
- package/src/cli/commands/mcp.ts +1219 -0
- package/src/cli/commands/search.ts +285 -0
- package/src/cli/commands/serve.ts +348 -0
- package/src/cli/commands/summary.ts +60 -0
- package/src/cli/commands/teach.ts +86 -0
- package/src/cli/commands/text-search.ts +134 -0
- package/src/cli/commands/validate.ts +126 -64
- package/src/cli/index.ts +88 -19
- package/src/cli/load-collection.ts +144 -17
- package/src/cli/registry.ts +28 -0
- package/src/collection.ts +361 -10
- package/src/define-model.ts +101 -4
- package/src/document.ts +47 -1
- package/src/extract-sections.ts +1 -1
- package/src/index.ts +14 -2
- package/src/model-instance.ts +35 -5
- package/src/node-shortcuts.ts +1 -1
- package/src/query/collection-query.ts +96 -9
- package/src/query/index.ts +7 -0
- package/src/query/query-dsl.ts +259 -0
- package/src/relationships/has-many.ts +39 -0
- package/src/relationships/index.ts +7 -2
- package/src/section.ts +2 -0
- package/src/types.ts +23 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/match-pattern.ts +65 -0
- package/src/validator.ts +18 -1
- package/test/collection.test.ts +118 -2
- package/test/fixtures/sdlc/MODELS.md +106 -0
- package/test/fixtures/sdlc/SKILL.md +404 -0
- package/test/fixtures/sdlc/index.ts +9 -0
- package/test/fixtures/sdlc/models.ts +8 -6
- package/test/fixtures/sdlc/stories/authentication/a-user-should-be-able-to-login.mdx +20 -0
- package/test/fixtures/sdlc/templates/epic.md +23 -0
- package/test/fixtures/sdlc/templates/story.md +19 -0
- package/test/pattern.test.ts +191 -0
- package/test/query-dsl.test.ts +431 -0
- package/test/query.test.ts +29 -0
- package/test/relationships.test.ts +130 -0
- package/test/section.test.ts +61 -0
- package/test/table-of-contents.test.ts +49 -5
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# MCP Tool & Resource Descriptions Review
|
|
2
|
+
|
|
3
|
+
Edit the descriptions below. I'll incorporate your changes back into `src/cli/commands/mcp.ts`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
### `read_me`
|
|
10
|
+
```
|
|
11
|
+
Returns the content collection guide. Call this BEFORE working with any documents.
|
|
12
|
+
Contains model definitions, available tools, query syntax, and recommended workflow.
|
|
13
|
+
Call this at the start of every session to understand the collection structure.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### `inspect`
|
|
17
|
+
```
|
|
18
|
+
Overview of the collection — registered models, document count, available actions. Call `read_me` first if this is your first interaction.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### `get_model_info`
|
|
22
|
+
```
|
|
23
|
+
Get detailed information about a single model — fields, sections, relationships, example document. Use when you need to understand a model before creating or editing its documents.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### `list_documents`
|
|
27
|
+
```
|
|
28
|
+
List all document path IDs in the collection, optionally filtered by model name or prefix. The prefix before the slash indicates the model.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### `query`
|
|
32
|
+
```
|
|
33
|
+
Query typed model instances with MongoDB-style filtering. See `read_me` output for full syntax reference. Where clause: keys are dot-notation paths, values are literals (implies $eq), arrays (implies $in), or operator objects like { "$gt": 5 }. Operators: $eq, $neq, $in, $notIn, $gt, $lt, $gte, $lte, $contains, $startsWith, $endsWith, $regex, $exists. Supports sort, limit, offset, select, and method (fetchAll/first/last/count).
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### `search_content`
|
|
37
|
+
```
|
|
38
|
+
Full-text regex search across all document content. Returns matching document IDs with context. Searches document body text, not metadata — for metadata filtering, use `query` instead.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `text_search`
|
|
42
|
+
```
|
|
43
|
+
Search file contents with pattern matching using ripgrep. Returns distinct file matches by default, or line-level detail with expanded=true.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `validate`
|
|
47
|
+
```
|
|
48
|
+
Validate a document against its model schema. Returns validation result with any errors. **ALWAYS call after create/update operations** to confirm the document conforms to its model.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `create_document`
|
|
52
|
+
```
|
|
53
|
+
**ALWAYS use this instead of writing markdown files directly.** Creates a new document with proper scaffolding from a model definition — generates correct frontmatter defaults and section headings. Call `validate` after creation.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `update_document`
|
|
57
|
+
```
|
|
58
|
+
Update a document's frontmatter and/or replace its entire content body. Use for frontmatter changes. For section-level edits, prefer `update_section` instead. Call `validate` after.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### `update_section`
|
|
62
|
+
```
|
|
63
|
+
Preferred way to edit document content. Surgically edit a specific section — replace, append, or remove. Target a section by its heading name. Call `validate` after.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `delete_document`
|
|
67
|
+
```
|
|
68
|
+
Delete a document from the collection permanently. Cannot be undone except through version control.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `run_action`
|
|
72
|
+
```
|
|
73
|
+
Execute a registered collection action by name.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Resources
|
|
79
|
+
|
|
80
|
+
### `contentbase://schema`
|
|
81
|
+
```
|
|
82
|
+
JSON schema of all registered models — fields, sections, relationships, computed properties
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `contentbase://toc`
|
|
86
|
+
```
|
|
87
|
+
Markdown table of contents for all documents in the collection
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `contentbase://models-summary`
|
|
91
|
+
```
|
|
92
|
+
Generated MODELS.md describing all model definitions with attributes, sections, and relationships
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `contentbase://primer`
|
|
96
|
+
```
|
|
97
|
+
Combined teach output — models summary, table of contents, CLI reference, and API primer
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Prompts
|
|
103
|
+
|
|
104
|
+
### `create-{model}` (per-model)
|
|
105
|
+
```
|
|
106
|
+
Guide creation of a new {Model} document with proper schema and sections
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `review-document`
|
|
110
|
+
```
|
|
111
|
+
Fetch a document, run validation, and present it for review
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `teach`
|
|
115
|
+
```
|
|
116
|
+
Full contentbase documentation — models, table of contents, CLI reference, and API primer. For a quick-start behavioral guide, use the `read_me` tool instead.
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `query-guide`
|
|
120
|
+
```
|
|
121
|
+
Show available models, fields, and query operators to help build queries
|
|
122
|
+
```
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# Contentbase MCP Server Design
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The server wraps a loaded `Collection` instance and exposes its capabilities through the three MCP primitives. The key design principle: **Resources are for reading, Tools are for writing and computing, Prompts are for guiding LLM workflows.**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Resources
|
|
10
|
+
|
|
11
|
+
Resources expose collection data as readable URIs. MCP supports both static resources and URI templates for dynamic access.
|
|
12
|
+
|
|
13
|
+
### Static Resources
|
|
14
|
+
|
|
15
|
+
| URI | Description | Backed by |
|
|
16
|
+
|-----|-------------|-----------|
|
|
17
|
+
| `contentbase://schema` | All registered models with their meta fields, sections, relationships, computed properties, and defaults | `collection.modelDefinitions` + `introspectMetaSchema()` |
|
|
18
|
+
| `contentbase://toc` | Table of contents listing all documents grouped by model | `collection.tableOfContents()` |
|
|
19
|
+
| `contentbase://models-summary` | Full MODELS.md documentation | `collection.generateModelSummary()` |
|
|
20
|
+
| `contentbase://primer` | The teach output — full LLM context document | Same as `cnotes teach` |
|
|
21
|
+
|
|
22
|
+
The `schema` resource is the most important one. It's what an LLM reads first to understand what models exist, what fields they have, and how to query them. Structure it as JSON:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"name": "my-project",
|
|
27
|
+
"rootPath": "/path/to/content",
|
|
28
|
+
"models": [
|
|
29
|
+
{
|
|
30
|
+
"name": "Epic",
|
|
31
|
+
"prefix": "epics",
|
|
32
|
+
"meta": [
|
|
33
|
+
{ "name": "priority", "type": "enum", "values": ["low","medium","high"], "required": false, "description": "Importance level" },
|
|
34
|
+
{ "name": "status", "type": "enum", "values": ["created","in-progress","complete"], "required": false, "default": "created" }
|
|
35
|
+
],
|
|
36
|
+
"sections": {},
|
|
37
|
+
"relationships": {
|
|
38
|
+
"stories": { "type": "hasMany", "target": "Story", "heading": "Stories" }
|
|
39
|
+
},
|
|
40
|
+
"computed": ["isComplete"],
|
|
41
|
+
"defaults": { "status": "created" },
|
|
42
|
+
"documentCount": 2
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"actions": ["rebuild-index"],
|
|
46
|
+
"totalDocuments": 15
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Dynamic Resources (URI Templates)
|
|
51
|
+
|
|
52
|
+
| URI Template | Description | Backed by |
|
|
53
|
+
|--------------|-------------|-----------|
|
|
54
|
+
| `contentbase://documents/{pathId}` | Raw document — id, title, meta, content, outline | `collection.document(pathId).toJSON()` + `.toOutline()` |
|
|
55
|
+
| `contentbase://models/{modelName}/{pathId}` | Typed model instance — meta, sections, computed, validation | `collection.getModel(pathId, def).toJSON({ sections: [...all], computed: [...all] })` |
|
|
56
|
+
| `contentbase://documents/{pathId}/sections/{sectionName}` | A single extracted section's content | `doc.querySection(heading)` stringified |
|
|
57
|
+
|
|
58
|
+
The `documents/{pathId}` resource returns the full document representation:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"id": "epics/authentication",
|
|
63
|
+
"title": "Authentication",
|
|
64
|
+
"slug": "authentication",
|
|
65
|
+
"meta": { "status": "in-progress", "priority": "high" },
|
|
66
|
+
"content": "# Authentication\n\n...",
|
|
67
|
+
"outline": "- Authentication\n - User Stories\n - Acceptance Criteria",
|
|
68
|
+
"model": "Epic"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The `models/{modelName}/{pathId}` resource adds the typed layer — parsed sections, resolved computed properties, and validation state. This is the "rich" version.
|
|
73
|
+
|
|
74
|
+
**Resource listing**: The server should implement `resources/list` to return all known documents as resources, so an LLM client can browse them. Enumerate `collection.available` and map each to its URI.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Tools
|
|
79
|
+
|
|
80
|
+
Tools handle writes, queries, and computations that go beyond simple data retrieval.
|
|
81
|
+
|
|
82
|
+
### Query Tools
|
|
83
|
+
|
|
84
|
+
**`query`** — The primary query interface. This is the most powerful tool.
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"name": "query",
|
|
89
|
+
"description": "Query documents of a specific model type with optional filters",
|
|
90
|
+
"inputSchema": {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"properties": {
|
|
93
|
+
"model": { "type": "string", "description": "Model name (e.g. 'Epic', 'Story')" },
|
|
94
|
+
"where": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"properties": {
|
|
99
|
+
"path": { "type": "string", "description": "Dot-path field (e.g. 'meta.status', 'title')" },
|
|
100
|
+
"operator": { "type": "string", "enum": ["eq","neq","gt","lt","gte","lte","in","notIn","contains","startsWith","endsWith","regex","exists","notExists"] },
|
|
101
|
+
"value": {}
|
|
102
|
+
},
|
|
103
|
+
"required": ["path"]
|
|
104
|
+
},
|
|
105
|
+
"description": "Filter conditions (AND logic)"
|
|
106
|
+
},
|
|
107
|
+
"select": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"properties": {
|
|
110
|
+
"sections": { "type": "array", "items": { "type": "string" } },
|
|
111
|
+
"computed": { "type": "array", "items": { "type": "string" } },
|
|
112
|
+
"related": { "type": "array", "items": { "type": "string" } }
|
|
113
|
+
},
|
|
114
|
+
"description": "Which optional fields to include in results"
|
|
115
|
+
},
|
|
116
|
+
"limit": { "type": "string", "enum": ["all", "first", "last", "count"] }
|
|
117
|
+
},
|
|
118
|
+
"required": ["model"]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Implementation maps directly to the `CollectionQuery` chain:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
let q = collection.query(modelDef);
|
|
127
|
+
for (const condition of input.where ?? []) {
|
|
128
|
+
q = q.where(condition.path, condition.operator ?? "eq", condition.value);
|
|
129
|
+
}
|
|
130
|
+
const results = await q[input.limit ?? "fetchAll"]();
|
|
131
|
+
return results.map(r => r.toJSON(input.select));
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**`search_content`** — Full-text search across document bodies.
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"name": "search_content",
|
|
139
|
+
"description": "Search document content and titles for a text pattern",
|
|
140
|
+
"inputSchema": {
|
|
141
|
+
"properties": {
|
|
142
|
+
"pattern": { "type": "string", "description": "Text or regex pattern to search for" },
|
|
143
|
+
"model": { "type": "string", "description": "Optional: limit to a specific model type" }
|
|
144
|
+
},
|
|
145
|
+
"required": ["pattern"]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Iterates `collection.documents`, calls `doc.toText()` or `doc.content`, and does string/regex matching. Returns matching document IDs with surrounding context snippets.
|
|
151
|
+
|
|
152
|
+
**`text_search`** — File-level text search using ripgrep.
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"name": "text_search",
|
|
157
|
+
"description": "Search file contents with pattern matching using ripgrep. Returns distinct file matches by default, or line-level detail with expanded=true.",
|
|
158
|
+
"inputSchema": {
|
|
159
|
+
"properties": {
|
|
160
|
+
"pattern": { "type": "string", "description": "Text or regex pattern to search for" },
|
|
161
|
+
"expanded": { "type": "boolean", "description": "Return line-level matches instead of just file paths", "default": false },
|
|
162
|
+
"include": { "type": "string", "description": "Glob filter (e.g. '*.md')" },
|
|
163
|
+
"exclude": { "type": "string", "description": "Glob filter (e.g. 'node_modules')" },
|
|
164
|
+
"ignoreCase": { "type": "boolean", "description": "Case insensitive search", "default": false },
|
|
165
|
+
"maxResults": { "type": "number", "description": "Limit number of results" }
|
|
166
|
+
},
|
|
167
|
+
"required": ["pattern"]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Returns `{ files: string[], count: number }` by default, or `{ files: [{ file, matches: [{ line, column?, content }] }], count }` when `expanded` is true.
|
|
173
|
+
|
|
174
|
+
### Validation Tools
|
|
175
|
+
|
|
176
|
+
**`validate`** — Validate one or more documents.
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"name": "validate",
|
|
181
|
+
"description": "Validate documents against their model schemas. Returns errors by document.",
|
|
182
|
+
"inputSchema": {
|
|
183
|
+
"properties": {
|
|
184
|
+
"target": { "type": "string", "description": "A pathId ('stories/login'), model name ('Story'), or 'all'" }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Maps to `validateDocument(doc, definition)` for individual docs, or iterates all instances of a model. Returns structured validation results with field-level errors.
|
|
191
|
+
|
|
192
|
+
### Mutation Tools
|
|
193
|
+
|
|
194
|
+
**`create_document`** — Create a new document for a model type.
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"name": "create_document",
|
|
199
|
+
"description": "Create a new markdown document for a model, with frontmatter and section scaffolding",
|
|
200
|
+
"inputSchema": {
|
|
201
|
+
"properties": {
|
|
202
|
+
"model": { "type": "string", "description": "Model name" },
|
|
203
|
+
"title": { "type": "string", "description": "Document title" },
|
|
204
|
+
"meta": { "type": "object", "description": "Frontmatter values (merged over defaults)" },
|
|
205
|
+
"content": { "type": "string", "description": "Optional: full markdown body (overrides scaffolding)" }
|
|
206
|
+
},
|
|
207
|
+
"required": ["model", "title"]
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Mirrors what `cnotes create` does: applies Zod defaults, merges with definition defaults, checks for templates, scaffolds section headings, writes via `collection.saveItem()`. Returns the new pathId and full document.
|
|
213
|
+
|
|
214
|
+
**`update_document`** — Modify an existing document's frontmatter or content.
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"name": "update_document",
|
|
219
|
+
"description": "Update a document's frontmatter metadata and/or markdown content",
|
|
220
|
+
"inputSchema": {
|
|
221
|
+
"properties": {
|
|
222
|
+
"pathId": { "type": "string" },
|
|
223
|
+
"meta": { "type": "object", "description": "Fields to merge into frontmatter" },
|
|
224
|
+
"content": { "type": "string", "description": "Replace the entire markdown body" }
|
|
225
|
+
},
|
|
226
|
+
"required": ["pathId"]
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Uses `doc.replaceContent()` and reconstructs frontmatter, then `doc.save()`.
|
|
232
|
+
|
|
233
|
+
**`update_section`** — Replace, append to, or remove a specific section.
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"name": "update_section",
|
|
238
|
+
"description": "Modify a specific section within a document",
|
|
239
|
+
"inputSchema": {
|
|
240
|
+
"properties": {
|
|
241
|
+
"pathId": { "type": "string" },
|
|
242
|
+
"heading": { "type": "string", "description": "Section heading text" },
|
|
243
|
+
"action": { "type": "string", "enum": ["replace", "append", "remove"] },
|
|
244
|
+
"content": { "type": "string", "description": "Markdown content (required for replace/append)" }
|
|
245
|
+
},
|
|
246
|
+
"required": ["pathId", "heading", "action"]
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Maps to `doc.replaceSectionContent()`, `doc.appendToSection()`, or `doc.removeSection()`, then `doc.save()`. This lets an LLM surgically edit one section without touching the rest of the document.
|
|
252
|
+
|
|
253
|
+
**`delete_document`** — Delete a document.
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
{
|
|
257
|
+
"name": "delete_document",
|
|
258
|
+
"description": "Permanently delete a document from the collection",
|
|
259
|
+
"inputSchema": {
|
|
260
|
+
"properties": {
|
|
261
|
+
"pathId": { "type": "string" }
|
|
262
|
+
},
|
|
263
|
+
"required": ["pathId"]
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Maps to `collection.deleteItem(pathId)`.
|
|
269
|
+
|
|
270
|
+
### Action Tools
|
|
271
|
+
|
|
272
|
+
**`run_action`** — Execute a registered collection action.
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"name": "run_action",
|
|
277
|
+
"description": "Run a named action registered on the collection",
|
|
278
|
+
"inputSchema": {
|
|
279
|
+
"properties": {
|
|
280
|
+
"name": { "type": "string", "description": "Action name" },
|
|
281
|
+
"args": { "type": "object", "description": "Arguments passed to the action" }
|
|
282
|
+
},
|
|
283
|
+
"required": ["name"]
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Maps directly to `collection.runAction(name, ...args)`.
|
|
289
|
+
|
|
290
|
+
### Introspection Tools
|
|
291
|
+
|
|
292
|
+
**`inspect`** — Get collection structure at a glance.
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"name": "inspect",
|
|
297
|
+
"description": "Get collection metadata: registered models, document counts, available actions",
|
|
298
|
+
"inputSchema": { "properties": {} }
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Returns the same structured info as `cnotes inspect` but as JSON. Useful as a first-call tool for an LLM to orient itself.
|
|
303
|
+
|
|
304
|
+
**`list_documents`** — List all document IDs, optionally filtered by model.
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"name": "list_documents",
|
|
309
|
+
"description": "List all document path IDs in the collection, optionally filtered by model",
|
|
310
|
+
"inputSchema": {
|
|
311
|
+
"properties": {
|
|
312
|
+
"model": { "type": "string", "description": "Optional model name to filter by" }
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Prompts
|
|
321
|
+
|
|
322
|
+
MCP prompts are reusable templates that guide LLM interactions. They can accept arguments and return structured messages.
|
|
323
|
+
|
|
324
|
+
### `create-{modelName}`
|
|
325
|
+
|
|
326
|
+
One prompt per registered model, dynamically generated at server startup.
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{
|
|
330
|
+
"name": "create-epic",
|
|
331
|
+
"description": "Guide for creating a new Epic document with proper schema and sections",
|
|
332
|
+
"arguments": [
|
|
333
|
+
{ "name": "title", "required": true, "description": "Title for the new epic" }
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
When resolved, returns a message like:
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
You are creating a new Epic document titled "{title}".
|
|
342
|
+
|
|
343
|
+
## Required Frontmatter
|
|
344
|
+
- status: enum (created | in-progress | complete), default: "created"
|
|
345
|
+
- priority: enum (low | medium | high), optional
|
|
346
|
+
|
|
347
|
+
## Sections to Include
|
|
348
|
+
This model has a "Stories" section (hasMany relationship).
|
|
349
|
+
Include a ## Stories heading with ### sub-headings for each story.
|
|
350
|
+
|
|
351
|
+
## Template
|
|
352
|
+
If a template exists at templates/Epic.mdx, its structure should be followed.
|
|
353
|
+
|
|
354
|
+
Please draft the document content, then I'll create it using the create_document tool.
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Built from `introspectMetaSchema(def.meta)` + the sections/relationships on the definition. The prompt teaches the LLM the shape of the model so it can draft well-formed content.
|
|
358
|
+
|
|
359
|
+
### `review-document`
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
{
|
|
363
|
+
"name": "review-document",
|
|
364
|
+
"description": "Review a document for schema compliance, completeness, and quality",
|
|
365
|
+
"arguments": [
|
|
366
|
+
{ "name": "pathId", "required": true }
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Resolves by fetching the document, running validation, and building a prompt like:
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
Review the document "{title}" ({pathId}).
|
|
375
|
+
|
|
376
|
+
## Model: Story
|
|
377
|
+
## Validation: 1 error
|
|
378
|
+
- meta.status: Invalid enum value
|
|
379
|
+
|
|
380
|
+
## Current Frontmatter
|
|
381
|
+
status: "unknown"
|
|
382
|
+
epic: "authentication"
|
|
383
|
+
|
|
384
|
+
## Current Sections
|
|
385
|
+
- Acceptance Criteria: 3 items
|
|
386
|
+
- Mockups: 0 items (empty)
|
|
387
|
+
|
|
388
|
+
## Document Content
|
|
389
|
+
[full content here]
|
|
390
|
+
|
|
391
|
+
Please suggest fixes for validation errors and check that all sections are complete.
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### `teach`
|
|
395
|
+
|
|
396
|
+
```json
|
|
397
|
+
{
|
|
398
|
+
"name": "teach",
|
|
399
|
+
"description": "Get full collection documentation for LLM context (models, API, CLI reference)",
|
|
400
|
+
"arguments": []
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Returns the same output as `cnotes teach` — MODELS.md + TOC + CLI.md + PRIMER.md. This is the "onboarding" prompt: paste it in and the LLM understands your entire content structure.
|
|
405
|
+
|
|
406
|
+
### `query-guide`
|
|
407
|
+
|
|
408
|
+
```json
|
|
409
|
+
{
|
|
410
|
+
"name": "query-guide",
|
|
411
|
+
"description": "Help construct a query against the collection",
|
|
412
|
+
"arguments": [
|
|
413
|
+
{ "name": "intent", "required": true, "description": "What you're trying to find (natural language)" }
|
|
414
|
+
]
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Returns available models, their filterable fields, and operator reference so the LLM can construct a proper `query` tool call.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Architecture Opinions
|
|
423
|
+
|
|
424
|
+
**1. One collection per server instance.** The server takes a content folder path at startup, loads the collection once, and serves it. If you have multiple content collections, run multiple server instances. This keeps URIs clean and avoids routing complexity.
|
|
425
|
+
|
|
426
|
+
**2. Auto-register prompts from model definitions.** At startup, iterate `collection.modelDefinitions` and generate a `create-{name}` prompt for each. This means adding a new model to your content automatically gives the MCP server a new prompt — zero config.
|
|
427
|
+
|
|
428
|
+
**3. `query` is the star tool.** Most LLM interactions will flow through it. Make it flexible (all operators, all field paths) but keep the response lean — default to just `{ id, title, meta }` and require explicit `select` for sections/computed/relationships. This avoids blowing up context with data the LLM didn't ask for.
|
|
429
|
+
|
|
430
|
+
**4. Resource subscriptions for file watching.** MCP supports resource change notifications. Wire up a file watcher on `rootPath` and call `collection.load({ refresh: true })` on changes, then emit `notifications/resources/updated`. This makes the server reactive — edit a markdown file in your editor and the MCP client sees the update immediately.
|
|
431
|
+
|
|
432
|
+
**5. `update_section` is more valuable than `update_document`.** LLMs work better with surgical edits. An LLM that can say "append this acceptance criterion to the Acceptance Criteria section of stories/login" is more useful (and less error-prone) than one that rewrites the entire document body. Lean into the section-based architecture.
|
|
433
|
+
|
|
434
|
+
**6. Lean on `teach` for bootstrapping.** When an MCP client first connects, the `teach` prompt gives it everything it needs. This is unique to contentbase — most MCP servers don't have a built-in "here's how everything works" document. The primer + models summary is a competitive advantage.
|
|
435
|
+
|
|
436
|
+
**7. Validation as a guardrail, not a gate.** The `validate` tool should return structured errors, but `create_document` and `update_document` should save even if validation fails (matching contentbase's current behavior where Zod defaults are applied but documents aren't rejected). Let the LLM decide whether to fix errors.
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## Suggested Package Structure
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
contentbase-mcp/
|
|
444
|
+
src/
|
|
445
|
+
server.ts — MCP server setup, collection loading
|
|
446
|
+
resources.ts — Resource handlers (document, schema, toc)
|
|
447
|
+
tools.ts — Tool handlers (query, validate, create, update, etc.)
|
|
448
|
+
prompts.ts — Prompt generators (create-*, review, teach)
|
|
449
|
+
file-watcher.ts — FS watcher for resource change notifications
|
|
450
|
+
package.json — depends on "contentbase" as a peer dependency
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
The server entry point takes a content folder path and optionally a collection module path (for custom index.ts setups). Mirror the CLI's 3-tier resolution for collection loading since that logic already exists in `src/cli/load-collection.ts` — worth extracting it into the core package so both CLI and MCP server share it.
|