@metaneutrons/doc-tools-mcp 0.1.0
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/LICENSE +674 -0
- package/README.md +118 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/bib/index.d.ts +2 -0
- package/dist/providers/bib/index.js +166 -0
- package/dist/providers/bib/index.js.map +1 -0
- package/dist/providers/bib/schema.d.ts +44 -0
- package/dist/providers/bib/schema.js +89 -0
- package/dist/providers/bib/schema.js.map +1 -0
- package/dist/providers/bib/store.d.ts +17 -0
- package/dist/providers/bib/store.js +90 -0
- package/dist/providers/bib/store.js.map +1 -0
- package/dist/providers/bib/tools/index.d.ts +2 -0
- package/dist/providers/bib/tools/index.js +4 -0
- package/dist/providers/bib/tools/index.js.map +1 -0
- package/dist/providers/bib/tools/read.d.ts +5 -0
- package/dist/providers/bib/tools/read.js +48 -0
- package/dist/providers/bib/tools/read.js.map +1 -0
- package/dist/providers/bib/tools/write.d.ts +2 -0
- package/dist/providers/bib/tools/write.js +66 -0
- package/dist/providers/bib/tools/write.js.map +1 -0
- package/dist/shared/errors.d.ts +34 -0
- package/dist/shared/errors.js +60 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/logger.d.ts +17 -0
- package/dist/shared/logger.js +40 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/types.d.ts +23 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +84 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Doc Tools MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://github.com/metaneutrons/doc-tools-mcp/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@metaneutrons/doc-tools-mcp)
|
|
5
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://modelcontextprotocol.io/)
|
|
9
|
+
|
|
10
|
+
> **⚠️ Work in Progress** — APIs may change without notice.
|
|
11
|
+
|
|
12
|
+
A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server for managing [pandoc](https://pandoc.org/)/CSL-YAML bibliographies. Lets your LLM read, search, add, update, delete, and validate citation entries directly.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @metaneutrons/doc-tools-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Add to your MCP client config (e.g., `claude_desktop_config.json`):
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"doc-tools": {
|
|
26
|
+
"command": "npx",
|
|
27
|
+
"args": ["-y", "@metaneutrons/doc-tools-mcp"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
> All `bib:*` tools take a `file` parameter — the path to your CSL-YAML bibliography file.
|
|
34
|
+
> The LLM discovers this automatically from `bibliography:` in `pandoc.yaml` or the YAML frontmatter of your `.md` files.
|
|
35
|
+
|
|
36
|
+
## Tools
|
|
37
|
+
|
|
38
|
+
### Read
|
|
39
|
+
|
|
40
|
+
| Tool | Description |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| `bib:get` | Retrieve a single entry by ID (full YAML block) |
|
|
43
|
+
| `bib:search` | Full-text search across all fields: author, title, type, year, editor, container-title |
|
|
44
|
+
| `bib:list` | List all entries of a given type (e.g., `legal_case`, `chapter`, `article-journal`) |
|
|
45
|
+
| `bib:exists` | Check if an ID exists (fast boolean check before citing) |
|
|
46
|
+
| `bib:stats` | Entry count total and breakdown by CSL type |
|
|
47
|
+
|
|
48
|
+
### Write
|
|
49
|
+
|
|
50
|
+
| Tool | Description |
|
|
51
|
+
|------|-------------|
|
|
52
|
+
| `bib:add` | Add a new entry with duplicate ID check, required field validation per CSL type, and YAML confirmation output |
|
|
53
|
+
| `bib:update` | Patch individual fields of an existing entry (other fields remain untouched) |
|
|
54
|
+
| `bib:delete` | Remove an entry by ID |
|
|
55
|
+
|
|
56
|
+
### Validation
|
|
57
|
+
|
|
58
|
+
| Tool | Description |
|
|
59
|
+
|------|-------------|
|
|
60
|
+
| `bib:validate` | Check entire file: YAML syntax, required fields per CSL type, duplicate IDs, missing `issued` dates |
|
|
61
|
+
|
|
62
|
+
All write operations automatically create a `.bak` backup before modifying the file.
|
|
63
|
+
|
|
64
|
+
## Supported CSL Types
|
|
65
|
+
|
|
66
|
+
Required field validation is provided for these types:
|
|
67
|
+
|
|
68
|
+
| Type | Required Fields |
|
|
69
|
+
|------|----------------|
|
|
70
|
+
| `legal_case` | `title`, `authority`, `number`, `issued` |
|
|
71
|
+
| `book` | `title`, `issued` |
|
|
72
|
+
| `article-journal` | `title`, `container-title`, `issued` |
|
|
73
|
+
| `chapter` | `title`, `container-title`, `issued` |
|
|
74
|
+
| `legislation` | `title`, `issued` |
|
|
75
|
+
| `thesis` | `title`, `issued` |
|
|
76
|
+
|
|
77
|
+
Other types are accepted without required field validation.
|
|
78
|
+
|
|
79
|
+
## Architecture
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
src/
|
|
83
|
+
├── index.ts # MCP server with dynamic provider loading
|
|
84
|
+
├── shared/
|
|
85
|
+
│ ├── types.ts # Provider interface, ToolDefinition, ToolResult
|
|
86
|
+
│ ├── logger.ts # Structured logging (pino → stderr)
|
|
87
|
+
│ └── errors.ts # Typed error hierarchy
|
|
88
|
+
└── providers/
|
|
89
|
+
└── bib/ # Bibliography provider
|
|
90
|
+
├── index.ts # Provider implementation (9 tool handlers)
|
|
91
|
+
├── store.ts # YAML read/write with .bak backup
|
|
92
|
+
├── schema.ts # Zod schemas + per-type field validation
|
|
93
|
+
└── tools/ # Tool definitions (read + write)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The provider system is extensible — add a new directory under `src/providers/` with a `createProvider()` export and it will be auto-discovered at startup.
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm install # Install dependencies
|
|
102
|
+
npm run build # Compile TypeScript
|
|
103
|
+
npm test # Run tests
|
|
104
|
+
npm run test:watch # Watch mode
|
|
105
|
+
npm run test:coverage # Coverage report
|
|
106
|
+
npm run lint # ESLint
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Commit Convention
|
|
110
|
+
|
|
111
|
+
This repo uses [Conventional Commits](https://www.conventionalcommits.org/) enforced via Husky + commitlint.
|
|
112
|
+
|
|
113
|
+
**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `build`, `revert`
|
|
114
|
+
**Scopes:** `bib`, `core`, `deps`, `config`
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
GPL-3.0 — See [LICENSE](LICENSE) for details.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { rootLogger } from './shared/logger.js';
|
|
6
|
+
import { readFileSync, readdirSync } from 'fs';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
12
|
+
if (process.argv.includes('--version') || process.argv.includes('-v')) {
|
|
13
|
+
console.log(packageJson.version);
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
17
|
+
console.log(`
|
|
18
|
+
Doc Tools MCP Server v${packageJson.version}
|
|
19
|
+
|
|
20
|
+
A Model Context Protocol server for managing pandoc/CSL-YAML bibliographies.
|
|
21
|
+
|
|
22
|
+
USAGE:
|
|
23
|
+
node dist/index.js [OPTIONS]
|
|
24
|
+
|
|
25
|
+
OPTIONS:
|
|
26
|
+
-h, --help Print this help message
|
|
27
|
+
-v, --version Print version number
|
|
28
|
+
`);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
// Provider registry
|
|
32
|
+
const providers = new Map();
|
|
33
|
+
async function registerProvider(provider) {
|
|
34
|
+
providers.set(provider.name, provider);
|
|
35
|
+
if (provider.initialize)
|
|
36
|
+
await provider.initialize();
|
|
37
|
+
}
|
|
38
|
+
function getAllTools() {
|
|
39
|
+
return Array.from(providers.values()).flatMap((p) => p.getTools());
|
|
40
|
+
}
|
|
41
|
+
async function handleToolCall(toolName, args) {
|
|
42
|
+
const colonIndex = toolName.indexOf(':');
|
|
43
|
+
if (colonIndex === -1) {
|
|
44
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${toolName}` }], isError: true };
|
|
45
|
+
}
|
|
46
|
+
const prefix = toolName.substring(0, colonIndex);
|
|
47
|
+
const provider = providers.get(prefix);
|
|
48
|
+
if (!provider) {
|
|
49
|
+
return { content: [{ type: 'text', text: `Unknown provider: ${prefix}` }], isError: true };
|
|
50
|
+
}
|
|
51
|
+
return provider.handleToolCall(toolName, args);
|
|
52
|
+
}
|
|
53
|
+
// Create MCP server
|
|
54
|
+
const server = new Server({ name: 'doc-tools-mcp', version: packageJson.version }, { capabilities: { tools: {} } });
|
|
55
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
56
|
+
tools: getAllTools().map((tool) => ({
|
|
57
|
+
name: tool.name,
|
|
58
|
+
description: tool.description,
|
|
59
|
+
inputSchema: tool.inputSchema.toJSONSchema(),
|
|
60
|
+
})),
|
|
61
|
+
}));
|
|
62
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
63
|
+
const { name, arguments: args } = request.params;
|
|
64
|
+
const startTime = Date.now();
|
|
65
|
+
rootLogger.info('Tool call', { tool: name });
|
|
66
|
+
try {
|
|
67
|
+
const result = await handleToolCall(name, args ?? {});
|
|
68
|
+
rootLogger.info('Tool call completed', { tool: name, duration: Date.now() - startTime });
|
|
69
|
+
return { content: result.content, isError: result.isError };
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
rootLogger.error('Tool call failed', error, { tool: name, duration: Date.now() - startTime });
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// Graceful shutdown
|
|
80
|
+
let isShuttingDown = false;
|
|
81
|
+
async function cleanup(signal) {
|
|
82
|
+
if (isShuttingDown)
|
|
83
|
+
return;
|
|
84
|
+
isShuttingDown = true;
|
|
85
|
+
rootLogger.info('Shutdown', { signal });
|
|
86
|
+
for (const provider of providers.values()) {
|
|
87
|
+
await provider.shutdown();
|
|
88
|
+
}
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
process.on('SIGINT', () => cleanup('SIGINT'));
|
|
92
|
+
process.on('SIGTERM', () => cleanup('SIGTERM'));
|
|
93
|
+
process.stdin.on('close', () => cleanup('stdin close'));
|
|
94
|
+
// Dynamic provider loading
|
|
95
|
+
const providersDir = join(__dirname, 'providers');
|
|
96
|
+
for (const entry of readdirSync(providersDir, { withFileTypes: true })) {
|
|
97
|
+
if (!entry.isDirectory())
|
|
98
|
+
continue;
|
|
99
|
+
try {
|
|
100
|
+
const mod = await import(`./providers/${entry.name}/index.js`);
|
|
101
|
+
if (typeof mod.createProvider === 'function') {
|
|
102
|
+
const provider = mod.createProvider();
|
|
103
|
+
if (provider) {
|
|
104
|
+
await registerProvider(provider);
|
|
105
|
+
rootLogger.info(`Provider registered: ${provider.name}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
rootLogger.error(`Failed to load provider: ${entry.name}`, error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const transport = new StdioServerTransport();
|
|
114
|
+
await server.connect(transport);
|
|
115
|
+
rootLogger.info('MCP server connected and ready');
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
import { rootLogger } from '../../shared/logger.js';
|
|
3
|
+
import { NotFoundError, DuplicateError } from '../../shared/errors.js';
|
|
4
|
+
import { BibStore } from './store.js';
|
|
5
|
+
import { validateRequiredFields, validateBibliography } from './schema.js';
|
|
6
|
+
import { bibTools } from './tools/index.js';
|
|
7
|
+
const logger = rootLogger.child({ module: 'bib' });
|
|
8
|
+
/** Serialize a single entry to YAML for display */
|
|
9
|
+
function entryToYaml(entry) {
|
|
10
|
+
return yaml.dump([entry], { indent: 2, lineWidth: -1, noRefs: true, quotingType: '"', forceQuotes: false }).trim();
|
|
11
|
+
}
|
|
12
|
+
/** Flatten all string values of an entry for full-text search */
|
|
13
|
+
function flattenEntry(entry) {
|
|
14
|
+
const parts = [];
|
|
15
|
+
const walk = (obj) => {
|
|
16
|
+
if (typeof obj === 'string')
|
|
17
|
+
parts.push(obj);
|
|
18
|
+
else if (typeof obj === 'number')
|
|
19
|
+
parts.push(String(obj));
|
|
20
|
+
else if (Array.isArray(obj))
|
|
21
|
+
obj.forEach(walk);
|
|
22
|
+
else if (obj && typeof obj === 'object')
|
|
23
|
+
Object.values(obj).forEach(walk);
|
|
24
|
+
};
|
|
25
|
+
walk(entry);
|
|
26
|
+
return parts.join(' ').toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
class BibProvider {
|
|
29
|
+
name = 'bib';
|
|
30
|
+
store = new BibStore();
|
|
31
|
+
getTools() {
|
|
32
|
+
return bibTools;
|
|
33
|
+
}
|
|
34
|
+
async handleToolCall(toolName, args) {
|
|
35
|
+
try {
|
|
36
|
+
switch (toolName) {
|
|
37
|
+
case 'bib:get': return await this.handleGet(args);
|
|
38
|
+
case 'bib:search': return await this.handleSearch(args);
|
|
39
|
+
case 'bib:list': return await this.handleList(args);
|
|
40
|
+
case 'bib:exists': return await this.handleExists(args);
|
|
41
|
+
case 'bib:stats': return await this.handleStats(args);
|
|
42
|
+
case 'bib:add': return await this.handleAdd(args);
|
|
43
|
+
case 'bib:update': return await this.handleUpdate(args);
|
|
44
|
+
case 'bib:delete': return await this.handleDelete(args);
|
|
45
|
+
case 'bib:validate': return await this.handleValidate(args);
|
|
46
|
+
default:
|
|
47
|
+
return { content: [{ type: 'text', text: `Unknown tool: ${toolName}` }], isError: true };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
logger.error('Tool call failed', error, { tool: toolName });
|
|
52
|
+
const message = error instanceof Error ? error.toString() : String(error);
|
|
53
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async shutdown() {
|
|
57
|
+
logger.info('Bib provider shutdown');
|
|
58
|
+
}
|
|
59
|
+
// --- Read tools ---
|
|
60
|
+
async handleGet(args) {
|
|
61
|
+
const { file, id } = args;
|
|
62
|
+
const entry = await this.store.get(file, id);
|
|
63
|
+
if (!entry)
|
|
64
|
+
throw new NotFoundError(id);
|
|
65
|
+
return { content: [{ type: 'text', text: entryToYaml(entry) }] };
|
|
66
|
+
}
|
|
67
|
+
async handleSearch(args) {
|
|
68
|
+
const { file, query, limit = 20 } = args;
|
|
69
|
+
const entries = await this.store.read(file);
|
|
70
|
+
const q = query.toLowerCase();
|
|
71
|
+
const matches = entries.filter((e) => flattenEntry(e).includes(q)).slice(0, limit);
|
|
72
|
+
if (matches.length === 0) {
|
|
73
|
+
return { content: [{ type: 'text', text: `No entries found for "${query}"` }] };
|
|
74
|
+
}
|
|
75
|
+
const text = `Found ${matches.length} result(s) for "${query}":\n\n` +
|
|
76
|
+
matches.map((e) => `- **${e.id}** (${e.type}): ${e.title ?? '(no title)'}`).join('\n');
|
|
77
|
+
return { content: [{ type: 'text', text }] };
|
|
78
|
+
}
|
|
79
|
+
async handleList(args) {
|
|
80
|
+
const { file, type, limit = 50 } = args;
|
|
81
|
+
const entries = await this.store.read(file);
|
|
82
|
+
const matches = entries.filter((e) => e.type === type).slice(0, limit);
|
|
83
|
+
const total = entries.filter((e) => e.type === type).length;
|
|
84
|
+
if (matches.length === 0) {
|
|
85
|
+
return { content: [{ type: 'text', text: `No entries of type "${type}"` }] };
|
|
86
|
+
}
|
|
87
|
+
const text = `${total} entries of type "${type}"` +
|
|
88
|
+
(total > limit ? ` (showing first ${limit})` : '') + ':\n\n' +
|
|
89
|
+
matches.map((e) => `- **${e.id}**: ${e.title ?? '(no title)'}`).join('\n');
|
|
90
|
+
return { content: [{ type: 'text', text }] };
|
|
91
|
+
}
|
|
92
|
+
async handleExists(args) {
|
|
93
|
+
const { file, id } = args;
|
|
94
|
+
const exists = await this.store.exists(file, id);
|
|
95
|
+
return { content: [{ type: 'text', text: exists ? `✅ "${id}" exists` : `❌ "${id}" not found` }] };
|
|
96
|
+
}
|
|
97
|
+
async handleStats(args) {
|
|
98
|
+
const { file } = args;
|
|
99
|
+
const entries = await this.store.read(file);
|
|
100
|
+
const byType = {};
|
|
101
|
+
for (const e of entries) {
|
|
102
|
+
byType[e.type] = (byType[e.type] ?? 0) + 1;
|
|
103
|
+
}
|
|
104
|
+
const sorted = Object.entries(byType).sort(([, a], [, b]) => b - a);
|
|
105
|
+
const text = `Total entries: ${entries.length}\n\n` +
|
|
106
|
+
sorted.map(([type, count]) => `- ${type}: ${count}`).join('\n');
|
|
107
|
+
return { content: [{ type: 'text', text }] };
|
|
108
|
+
}
|
|
109
|
+
// --- Write tools ---
|
|
110
|
+
async handleAdd(args) {
|
|
111
|
+
const { file, entry } = args;
|
|
112
|
+
// Duplicate check
|
|
113
|
+
if (await this.store.exists(file, entry.id)) {
|
|
114
|
+
throw new DuplicateError(entry.id);
|
|
115
|
+
}
|
|
116
|
+
// Required field validation
|
|
117
|
+
const issues = validateRequiredFields(entry);
|
|
118
|
+
if (issues.some((i) => i.severity === 'error')) {
|
|
119
|
+
const text = 'Validation errors:\n' + issues.map((i) => `- ${i.message}`).join('\n');
|
|
120
|
+
return { content: [{ type: 'text', text }], isError: true };
|
|
121
|
+
}
|
|
122
|
+
await this.store.add(file, entry);
|
|
123
|
+
const text = `✅ Entry "${entry.id}" added.\n\n${entryToYaml(entry)}`;
|
|
124
|
+
return { content: [{ type: 'text', text }] };
|
|
125
|
+
}
|
|
126
|
+
async handleUpdate(args) {
|
|
127
|
+
const { file, id, fields } = args;
|
|
128
|
+
if (!(await this.store.exists(file, id))) {
|
|
129
|
+
throw new NotFoundError(id);
|
|
130
|
+
}
|
|
131
|
+
const updated = await this.store.update(file, id, fields);
|
|
132
|
+
const text = `✅ Entry "${id}" updated.\n\n${entryToYaml(updated)}`;
|
|
133
|
+
return { content: [{ type: 'text', text }] };
|
|
134
|
+
}
|
|
135
|
+
async handleDelete(args) {
|
|
136
|
+
const { file, id } = args;
|
|
137
|
+
if (!(await this.store.exists(file, id))) {
|
|
138
|
+
throw new NotFoundError(id);
|
|
139
|
+
}
|
|
140
|
+
await this.store.delete(file, id);
|
|
141
|
+
return { content: [{ type: 'text', text: `✅ Entry "${id}" deleted.` }] };
|
|
142
|
+
}
|
|
143
|
+
async handleValidate(args) {
|
|
144
|
+
const { file } = args;
|
|
145
|
+
const entries = await this.store.read(file);
|
|
146
|
+
const issues = validateBibliography(entries);
|
|
147
|
+
if (issues.length === 0) {
|
|
148
|
+
return { content: [{ type: 'text', text: `✅ Bibliography is valid. ${entries.length} entries, no issues found.` }] };
|
|
149
|
+
}
|
|
150
|
+
const errors = issues.filter((i) => i.severity === 'error');
|
|
151
|
+
const warnings = issues.filter((i) => i.severity === 'warning');
|
|
152
|
+
let text = `Validation results for ${entries.length} entries:\n`;
|
|
153
|
+
text += `- ${errors.length} error(s), ${warnings.length} warning(s)\n\n`;
|
|
154
|
+
if (errors.length > 0) {
|
|
155
|
+
text += '**Errors:**\n' + errors.map((i) => `- [${i.id}] ${i.message}`).join('\n') + '\n\n';
|
|
156
|
+
}
|
|
157
|
+
if (warnings.length > 0) {
|
|
158
|
+
text += '**Warnings:**\n' + warnings.map((i) => `- [${i.id}] ${i.message}`).join('\n');
|
|
159
|
+
}
|
|
160
|
+
return { content: [{ type: 'text', text }] };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export function createProvider() {
|
|
164
|
+
return new BibProvider();
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/bib/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** All CSL types we validate required fields for */
|
|
3
|
+
export declare const CSL_TYPES: readonly ["article-journal", "book", "chapter", "legal_case", "legislation", "thesis", "report", "paper-conference", "webpage", "no-type"];
|
|
4
|
+
export type CslType = typeof CSL_TYPES[number];
|
|
5
|
+
/** Single CSL-YAML entry */
|
|
6
|
+
export declare const cslEntrySchema: z.ZodObject<{
|
|
7
|
+
id: z.ZodString;
|
|
8
|
+
type: z.ZodString;
|
|
9
|
+
title: z.ZodOptional<z.ZodString>;
|
|
10
|
+
author: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
11
|
+
family: z.ZodString;
|
|
12
|
+
given: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$loose>>>;
|
|
14
|
+
editor: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
15
|
+
family: z.ZodString;
|
|
16
|
+
given: z.ZodOptional<z.ZodString>;
|
|
17
|
+
}, z.core.$loose>>>;
|
|
18
|
+
issued: z.ZodOptional<z.ZodObject<{
|
|
19
|
+
'date-parts': z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodNumber>>>;
|
|
20
|
+
literal: z.ZodOptional<z.ZodString>;
|
|
21
|
+
}, z.core.$loose>>;
|
|
22
|
+
'container-title': z.ZodOptional<z.ZodString>;
|
|
23
|
+
volume: z.ZodOptional<z.ZodString>;
|
|
24
|
+
page: z.ZodOptional<z.ZodString>;
|
|
25
|
+
publisher: z.ZodOptional<z.ZodString>;
|
|
26
|
+
'publisher-place': z.ZodOptional<z.ZodString>;
|
|
27
|
+
edition: z.ZodOptional<z.ZodString>;
|
|
28
|
+
authority: z.ZodOptional<z.ZodString>;
|
|
29
|
+
number: z.ZodOptional<z.ZodString>;
|
|
30
|
+
genre: z.ZodOptional<z.ZodString>;
|
|
31
|
+
url: z.ZodOptional<z.ZodString>;
|
|
32
|
+
note: z.ZodOptional<z.ZodString>;
|
|
33
|
+
}, z.core.$loose>;
|
|
34
|
+
export type CslEntry = z.infer<typeof cslEntrySchema>;
|
|
35
|
+
export interface ValidationIssue {
|
|
36
|
+
id: string;
|
|
37
|
+
field?: string;
|
|
38
|
+
message: string;
|
|
39
|
+
severity: 'error' | 'warning';
|
|
40
|
+
}
|
|
41
|
+
/** Validate required fields for a given CSL type */
|
|
42
|
+
export declare function validateRequiredFields(entry: CslEntry): ValidationIssue[];
|
|
43
|
+
/** Validate an entire bibliography */
|
|
44
|
+
export declare function validateBibliography(entries: CslEntry[]): ValidationIssue[];
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** All CSL types we validate required fields for */
|
|
3
|
+
export const CSL_TYPES = [
|
|
4
|
+
'article-journal', 'book', 'chapter', 'legal_case', 'legislation',
|
|
5
|
+
'thesis', 'report', 'paper-conference', 'webpage', 'no-type',
|
|
6
|
+
];
|
|
7
|
+
/** CSL name variable (author, editor) */
|
|
8
|
+
const cslNameSchema = z.object({
|
|
9
|
+
family: z.string(),
|
|
10
|
+
given: z.string().optional(),
|
|
11
|
+
}).passthrough();
|
|
12
|
+
/** CSL date variable */
|
|
13
|
+
const cslDateSchema = z.object({
|
|
14
|
+
'date-parts': z.array(z.array(z.number())).optional(),
|
|
15
|
+
literal: z.string().optional(),
|
|
16
|
+
}).passthrough();
|
|
17
|
+
/** Single CSL-YAML entry */
|
|
18
|
+
export const cslEntrySchema = z.object({
|
|
19
|
+
id: z.string().min(1, 'ID must not be empty'),
|
|
20
|
+
type: z.string().min(1, 'Type must not be empty'),
|
|
21
|
+
title: z.string().optional(),
|
|
22
|
+
author: z.array(cslNameSchema).optional(),
|
|
23
|
+
editor: z.array(cslNameSchema).optional(),
|
|
24
|
+
issued: cslDateSchema.optional(),
|
|
25
|
+
'container-title': z.string().optional(),
|
|
26
|
+
volume: z.string().optional(),
|
|
27
|
+
page: z.string().optional(),
|
|
28
|
+
publisher: z.string().optional(),
|
|
29
|
+
'publisher-place': z.string().optional(),
|
|
30
|
+
edition: z.string().optional(),
|
|
31
|
+
authority: z.string().optional(),
|
|
32
|
+
number: z.string().optional(),
|
|
33
|
+
genre: z.string().optional(),
|
|
34
|
+
url: z.string().optional(),
|
|
35
|
+
note: z.string().optional(),
|
|
36
|
+
}).passthrough();
|
|
37
|
+
/** Required fields per CSL type */
|
|
38
|
+
const REQUIRED_FIELDS = {
|
|
39
|
+
'legal_case': ['title', 'authority', 'number', 'issued'],
|
|
40
|
+
'book': ['title', 'issued'],
|
|
41
|
+
'article-journal': ['title', 'container-title', 'issued'],
|
|
42
|
+
'chapter': ['title', 'container-title', 'issued'],
|
|
43
|
+
'legislation': ['title', 'issued'],
|
|
44
|
+
'thesis': ['title', 'issued'],
|
|
45
|
+
};
|
|
46
|
+
/** Validate required fields for a given CSL type */
|
|
47
|
+
export function validateRequiredFields(entry) {
|
|
48
|
+
const issues = [];
|
|
49
|
+
const required = REQUIRED_FIELDS[entry.type];
|
|
50
|
+
if (!required)
|
|
51
|
+
return issues;
|
|
52
|
+
for (const field of required) {
|
|
53
|
+
const value = entry[field];
|
|
54
|
+
if (value === undefined || value === null || value === '') {
|
|
55
|
+
issues.push({
|
|
56
|
+
id: entry.id,
|
|
57
|
+
field,
|
|
58
|
+
message: `Missing required field '${field}' for type '${entry.type}'`,
|
|
59
|
+
severity: 'error',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return issues;
|
|
64
|
+
}
|
|
65
|
+
/** Validate an entire bibliography */
|
|
66
|
+
export function validateBibliography(entries) {
|
|
67
|
+
const issues = [];
|
|
68
|
+
const seenIds = new Set();
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
// Duplicate ID check
|
|
71
|
+
if (seenIds.has(entry.id)) {
|
|
72
|
+
issues.push({ id: entry.id, message: `Duplicate ID '${entry.id}'`, severity: 'error' });
|
|
73
|
+
}
|
|
74
|
+
seenIds.add(entry.id);
|
|
75
|
+
// Required fields
|
|
76
|
+
issues.push(...validateRequiredFields(entry));
|
|
77
|
+
// Missing issued warning
|
|
78
|
+
if (!entry.issued) {
|
|
79
|
+
issues.push({
|
|
80
|
+
id: entry.id,
|
|
81
|
+
field: 'issued',
|
|
82
|
+
message: `Missing 'issued' date`,
|
|
83
|
+
severity: 'warning',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return issues;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/providers/bib/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CslEntry } from './schema.js';
|
|
2
|
+
export declare class BibStore {
|
|
3
|
+
/** Read and parse a CSL-YAML file */
|
|
4
|
+
read(filePath: string): Promise<CslEntry[]>;
|
|
5
|
+
/** Write entries back to a CSL-YAML file, creating a .bak backup first */
|
|
6
|
+
write(filePath: string, entries: CslEntry[]): Promise<void>;
|
|
7
|
+
/** Get a single entry by ID */
|
|
8
|
+
get(filePath: string, id: string): Promise<CslEntry | undefined>;
|
|
9
|
+
/** Check if an ID exists */
|
|
10
|
+
exists(filePath: string, id: string): Promise<boolean>;
|
|
11
|
+
/** Add a new entry (appends to end) */
|
|
12
|
+
add(filePath: string, entry: CslEntry): Promise<void>;
|
|
13
|
+
/** Update fields of an existing entry */
|
|
14
|
+
update(filePath: string, id: string, fields: Record<string, unknown>): Promise<CslEntry>;
|
|
15
|
+
/** Delete an entry by ID */
|
|
16
|
+
delete(filePath: string, id: string): Promise<void>;
|
|
17
|
+
}
|