fantsec-docmost-cli 2.2.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 +21 -0
- package/README.md +137 -0
- package/build/__tests__/cli-utils.test.js +287 -0
- package/build/__tests__/client-pagination.test.js +103 -0
- package/build/__tests__/discovery.test.js +40 -0
- package/build/__tests__/envelope.test.js +91 -0
- package/build/__tests__/filters.test.js +235 -0
- package/build/__tests__/integration/comment.test.js +48 -0
- package/build/__tests__/integration/discovery.test.js +24 -0
- package/build/__tests__/integration/file.test.js +33 -0
- package/build/__tests__/integration/group.test.js +48 -0
- package/build/__tests__/integration/helpers/global-setup.js +80 -0
- package/build/__tests__/integration/helpers/run-cli.js +163 -0
- package/build/__tests__/integration/invite.test.js +34 -0
- package/build/__tests__/integration/page.test.js +69 -0
- package/build/__tests__/integration/search.test.js +45 -0
- package/build/__tests__/integration/share.test.js +49 -0
- package/build/__tests__/integration/space.test.js +56 -0
- package/build/__tests__/integration/user.test.js +15 -0
- package/build/__tests__/integration/workspace.test.js +42 -0
- package/build/__tests__/markdown-converter.test.js +445 -0
- package/build/__tests__/mcp-tooling.test.js +58 -0
- package/build/__tests__/page-mentions.test.js +65 -0
- package/build/__tests__/tiptap-extensions.test.js +135 -0
- package/build/client.js +715 -0
- package/build/commands/comment.js +54 -0
- package/build/commands/discovery.js +21 -0
- package/build/commands/file.js +36 -0
- package/build/commands/group.js +91 -0
- package/build/commands/invite.js +67 -0
- package/build/commands/page.js +227 -0
- package/build/commands/search.js +33 -0
- package/build/commands/share.js +65 -0
- package/build/commands/space.js +154 -0
- package/build/commands/user.js +38 -0
- package/build/commands/workspace.js +77 -0
- package/build/index.js +19 -0
- package/build/lib/auth-utils.js +53 -0
- package/build/lib/cli-utils.js +293 -0
- package/build/lib/collaboration.js +126 -0
- package/build/lib/filters.js +137 -0
- package/build/lib/markdown-converter.js +187 -0
- package/build/lib/mcp-tooling.js +295 -0
- package/build/lib/page-mentions.js +162 -0
- package/build/lib/tiptap-extensions.js +86 -0
- package/build/mcp.js +186 -0
- package/build/program.js +60 -0
- package/package.json +64 -0
package/build/mcp.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
|
+
import { pathToFileURL } from "url";
|
|
7
|
+
import { getVersion } from "./program.js";
|
|
8
|
+
import { executeTool, listMcpTools, parseDocmostBearer, } from "./lib/mcp-tooling.js";
|
|
9
|
+
function formatExecutionResult(tool, result) {
|
|
10
|
+
if (result.parsed !== undefined) {
|
|
11
|
+
return JSON.stringify(result.parsed, null, 2);
|
|
12
|
+
}
|
|
13
|
+
if (tool.requiresOutputPath && result.outputPath) {
|
|
14
|
+
return JSON.stringify({
|
|
15
|
+
ok: result.ok,
|
|
16
|
+
data: {
|
|
17
|
+
outputPath: result.outputPath,
|
|
18
|
+
command: tool.commandName,
|
|
19
|
+
},
|
|
20
|
+
stderr: result.stderr.trim() || undefined,
|
|
21
|
+
}, null, 2);
|
|
22
|
+
}
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
ok: result.ok,
|
|
25
|
+
stdout: result.stdout.trim() || undefined,
|
|
26
|
+
stderr: result.stderr.trim() || undefined,
|
|
27
|
+
}, null, 2);
|
|
28
|
+
}
|
|
29
|
+
function createCatalogResource(tools) {
|
|
30
|
+
return {
|
|
31
|
+
tools: tools.map((tool) => ({
|
|
32
|
+
toolName: tool.toolName,
|
|
33
|
+
commandName: tool.commandName,
|
|
34
|
+
description: tool.description,
|
|
35
|
+
requiresOutputPath: tool.requiresOutputPath,
|
|
36
|
+
parameters: tool.options.map((option) => ({
|
|
37
|
+
name: option.name,
|
|
38
|
+
flag: option.long,
|
|
39
|
+
description: option.description,
|
|
40
|
+
required: option.required,
|
|
41
|
+
})),
|
|
42
|
+
})),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function createMcpServer() {
|
|
46
|
+
const tools = listMcpTools();
|
|
47
|
+
const server = new McpServer({
|
|
48
|
+
name: "docmost-mcp",
|
|
49
|
+
version: getVersion(),
|
|
50
|
+
}, {
|
|
51
|
+
capabilities: {
|
|
52
|
+
logging: {},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
const catalog = createCatalogResource(tools);
|
|
56
|
+
server.registerResource("docmost-tool-catalog", "docmost://commands", {
|
|
57
|
+
title: "Docmost tool catalog",
|
|
58
|
+
description: "A read-only catalog of Docmost MCP tools and their underlying CLI commands.",
|
|
59
|
+
mimeType: "application/json",
|
|
60
|
+
}, async () => ({
|
|
61
|
+
contents: [
|
|
62
|
+
{
|
|
63
|
+
uri: "docmost://commands",
|
|
64
|
+
mimeType: "application/json",
|
|
65
|
+
text: JSON.stringify(catalog, null, 2),
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
}));
|
|
69
|
+
for (const tool of tools) {
|
|
70
|
+
server.registerTool(tool.toolName, {
|
|
71
|
+
title: tool.toolName,
|
|
72
|
+
description: `${tool.description} (CLI: ${tool.commandName})`,
|
|
73
|
+
inputSchema: tool.inputSchema,
|
|
74
|
+
annotations: tool.annotations,
|
|
75
|
+
}, async (args, extra) => {
|
|
76
|
+
const authHeader = getAuthorizationHeader(extra?.requestInfo?.headers);
|
|
77
|
+
if (!authHeader) {
|
|
78
|
+
throw new Error("Authorization header is required for Docmost tool calls.");
|
|
79
|
+
}
|
|
80
|
+
const auth = parseDocmostBearer(extractBearerToken(authHeader));
|
|
81
|
+
const result = await executeTool(tool, args, auth);
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: formatExecutionResult(tool, result),
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
isError: !result.ok,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return server;
|
|
94
|
+
}
|
|
95
|
+
function getAuthorizationHeader(headers) {
|
|
96
|
+
if (!headers) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const value = headers.authorization ?? headers.Authorization;
|
|
100
|
+
return Array.isArray(value) ? value[0] : value;
|
|
101
|
+
}
|
|
102
|
+
function extractBearerToken(headerValue) {
|
|
103
|
+
if (!headerValue) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const trimmed = headerValue.trim();
|
|
107
|
+
const prefix = "Bearer ";
|
|
108
|
+
return trimmed.toLowerCase().startsWith(prefix.toLowerCase())
|
|
109
|
+
? trimmed.slice(prefix.length).trim()
|
|
110
|
+
: trimmed;
|
|
111
|
+
}
|
|
112
|
+
function createHttpApp() {
|
|
113
|
+
const app = createMcpExpressApp({ host: "0.0.0.0" });
|
|
114
|
+
app.get("/healthz", (_req, res) => {
|
|
115
|
+
res.json({ ok: true, service: "docmost-mcp", version: getVersion() });
|
|
116
|
+
});
|
|
117
|
+
app.post("/mcp", async (req, res) => {
|
|
118
|
+
const server = createMcpServer();
|
|
119
|
+
try {
|
|
120
|
+
const transport = new StreamableHTTPServerTransport({
|
|
121
|
+
sessionIdGenerator: undefined,
|
|
122
|
+
});
|
|
123
|
+
await server.connect(transport);
|
|
124
|
+
await transport.handleRequest(req, res, req.body);
|
|
125
|
+
res.on("close", () => {
|
|
126
|
+
void transport.close();
|
|
127
|
+
void server.close();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
132
|
+
if (!res.headersSent) {
|
|
133
|
+
res.status(500).json({
|
|
134
|
+
jsonrpc: "2.0",
|
|
135
|
+
error: {
|
|
136
|
+
code: -32603,
|
|
137
|
+
message,
|
|
138
|
+
},
|
|
139
|
+
id: null,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
app.get("/mcp", (_req, res) => {
|
|
145
|
+
res.status(405).json({
|
|
146
|
+
jsonrpc: "2.0",
|
|
147
|
+
error: {
|
|
148
|
+
code: -32000,
|
|
149
|
+
message: "Method not allowed.",
|
|
150
|
+
},
|
|
151
|
+
id: null,
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
app.delete("/mcp", (_req, res) => {
|
|
155
|
+
res.status(405).json({
|
|
156
|
+
jsonrpc: "2.0",
|
|
157
|
+
error: {
|
|
158
|
+
code: -32000,
|
|
159
|
+
message: "Method not allowed.",
|
|
160
|
+
},
|
|
161
|
+
id: null,
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
return app;
|
|
165
|
+
}
|
|
166
|
+
async function main() {
|
|
167
|
+
const mode = process.argv[2] || "stdio";
|
|
168
|
+
if (mode === "http") {
|
|
169
|
+
const port = Number(process.env.PORT || "8000");
|
|
170
|
+
const app = createHttpApp();
|
|
171
|
+
app.listen(port, "0.0.0.0", () => {
|
|
172
|
+
console.error(`Docmost MCP HTTP server listening on port ${port}`);
|
|
173
|
+
});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const server = createMcpServer();
|
|
177
|
+
const transport = new StdioServerTransport();
|
|
178
|
+
await server.connect(transport);
|
|
179
|
+
}
|
|
180
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
181
|
+
main().catch((error) => {
|
|
182
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
183
|
+
console.error(`Docmost MCP server error: ${message}`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
});
|
|
186
|
+
}
|
package/build/program.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { register as registerPageCommands } from "./commands/page.js";
|
|
4
|
+
import { register as registerWorkspaceCommands } from "./commands/workspace.js";
|
|
5
|
+
import { register as registerInviteCommands } from "./commands/invite.js";
|
|
6
|
+
import { register as registerUserCommands } from "./commands/user.js";
|
|
7
|
+
import { register as registerSpaceCommands } from "./commands/space.js";
|
|
8
|
+
import { register as registerGroupCommands } from "./commands/group.js";
|
|
9
|
+
import { register as registerCommentCommands } from "./commands/comment.js";
|
|
10
|
+
import { register as registerShareCommands } from "./commands/share.js";
|
|
11
|
+
import { register as registerFileCommands } from "./commands/file.js";
|
|
12
|
+
import { register as registerSearchCommands } from "./commands/search.js";
|
|
13
|
+
import { register as registerDiscoveryCommands } from "./commands/discovery.js";
|
|
14
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
15
|
+
export function createProgram() {
|
|
16
|
+
const program = new Command()
|
|
17
|
+
.name("docmost")
|
|
18
|
+
.description("Agent-first CLI for Docmost documentation platform")
|
|
19
|
+
.version(pkg.version)
|
|
20
|
+
.showHelpAfterError()
|
|
21
|
+
.option("-u, --api-url <url>", "Docmost API URL")
|
|
22
|
+
.option("-e, --email <email>", "Docmost account email")
|
|
23
|
+
.option("--password <password>", "Docmost account password (prefer DOCMOST_PASSWORD env var)")
|
|
24
|
+
.option("-t, --token <token>", "Docmost API auth token")
|
|
25
|
+
.option("-f, --format <format>", "Output format: json | table | text", "json")
|
|
26
|
+
.option("-q, --quiet", "Suppress output, exit code only")
|
|
27
|
+
.option("--limit <n>", "Items per API page (1-100)")
|
|
28
|
+
.option("--max-items <n>", "Stop after N total items")
|
|
29
|
+
.addHelpText("after", [
|
|
30
|
+
"",
|
|
31
|
+
"Examples:",
|
|
32
|
+
" docmost workspace-info",
|
|
33
|
+
" docmost page-list --space-id <space-id> --format table",
|
|
34
|
+
" docmost page-info --page-id <page-id> --format text",
|
|
35
|
+
" docmost search --query \"onboarding\"",
|
|
36
|
+
" docmost space-list --format table",
|
|
37
|
+
"",
|
|
38
|
+
"Auth precedence:",
|
|
39
|
+
" 1) --token, then DOCMOST_TOKEN",
|
|
40
|
+
" 2) --email/--password, then DOCMOST_EMAIL/DOCMOST_PASSWORD",
|
|
41
|
+
"",
|
|
42
|
+
"Security: CLI flags are visible in process lists. Use env vars for credentials.",
|
|
43
|
+
].join("\n"))
|
|
44
|
+
.exitOverride();
|
|
45
|
+
registerPageCommands(program);
|
|
46
|
+
registerWorkspaceCommands(program);
|
|
47
|
+
registerInviteCommands(program);
|
|
48
|
+
registerUserCommands(program);
|
|
49
|
+
registerSpaceCommands(program);
|
|
50
|
+
registerGroupCommands(program);
|
|
51
|
+
registerCommentCommands(program);
|
|
52
|
+
registerShareCommands(program);
|
|
53
|
+
registerFileCommands(program);
|
|
54
|
+
registerSearchCommands(program);
|
|
55
|
+
registerDiscoveryCommands(program);
|
|
56
|
+
return program;
|
|
57
|
+
}
|
|
58
|
+
export function getVersion() {
|
|
59
|
+
return pkg.version;
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fantsec-docmost-cli",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "A Docmost CLI and standard MCP server for documentation automation.",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"docmost": "build/index.js",
|
|
8
|
+
"docmost-mcp": "build/mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "tsc",
|
|
16
|
+
"start": "node build/index.js",
|
|
17
|
+
"start:mcp": "node build/mcp.js",
|
|
18
|
+
"start:mcp:http": "node build/mcp.js http",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
21
|
+
"test:all": "vitest run && vitest run --config vitest.integration.config.ts",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"watch": "tsc --watch"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"cli",
|
|
27
|
+
"docmost",
|
|
28
|
+
"documentation",
|
|
29
|
+
"ai",
|
|
30
|
+
"agent"
|
|
31
|
+
],
|
|
32
|
+
"author": "Moritz Krause",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"type": "module",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@hocuspocus/provider": "^3.4.4",
|
|
37
|
+
"@hocuspocus/transformer": "^3.4.4",
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
39
|
+
"@tiptap/core": "^3.18.0",
|
|
40
|
+
"@tiptap/extension-image": "^3.18.0",
|
|
41
|
+
"@tiptap/extension-link": "^3.18.0",
|
|
42
|
+
"@tiptap/extension-table": "^3.20.1",
|
|
43
|
+
"@tiptap/extension-table-cell": "^3.20.1",
|
|
44
|
+
"@tiptap/extension-table-header": "^3.20.1",
|
|
45
|
+
"@tiptap/extension-table-row": "^3.20.1",
|
|
46
|
+
"@tiptap/html": "^3.18.0",
|
|
47
|
+
"@tiptap/starter-kit": "^3.18.0",
|
|
48
|
+
"axios": "^1.6.0",
|
|
49
|
+
"commander": "^14.0.1",
|
|
50
|
+
"form-data": "^4.0.0",
|
|
51
|
+
"jsdom": "^27.4.0",
|
|
52
|
+
"marked": "^17.0.1",
|
|
53
|
+
"ws": "^8.19.0",
|
|
54
|
+
"yjs": "^13.6.29",
|
|
55
|
+
"zod": "^4.3.6"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/form-data": "^2.5.0",
|
|
59
|
+
"@types/jsdom": "^27.0.0",
|
|
60
|
+
"@types/node": "^20.0.0",
|
|
61
|
+
"typescript": "^5.0.0",
|
|
62
|
+
"vitest": "^4.0.18"
|
|
63
|
+
}
|
|
64
|
+
}
|