@docrouter/mcp 0.2.1 → 0.3.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/dist/index.mjs CHANGED
@@ -3,9 +3,9 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { z } from 'zod';
6
- import { DocRouterOrg } from '@docrouter/sdk';
7
- import { readFileSync } from 'fs';
8
- import { dirname, join } from 'path';
6
+ import { DocRouterAccount, DocRouterOrg } from '@docrouter/sdk';
7
+ import { readFileSync, existsSync, statSync, mkdirSync, writeFileSync } from 'fs';
8
+ import { isAbsolute, resolve, extname, basename, join, dirname } from 'path';
9
9
  import { fileURLToPath } from 'url';
10
10
 
11
11
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -16,11 +16,36 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
16
16
  });
17
17
  var ConfigSchema = z.object({
18
18
  baseURL: z.string().default("https://app.docrouter.ai/fastapi"),
19
- organizationId: z.string(),
20
19
  orgToken: z.string(),
21
20
  timeout: z.number().default(3e4),
22
21
  retries: z.number().default(3)
23
22
  });
23
+ function resolveKnowledgeBasePath(filename) {
24
+ const currentFile = fileURLToPath(import.meta.url);
25
+ const currentDir = dirname(currentFile);
26
+ if (currentDir.endsWith("dist")) {
27
+ return join(currentDir, "docs/knowledge_base", filename);
28
+ }
29
+ if (currentDir.endsWith("src")) {
30
+ const distPath = join(currentDir, "..", "dist", "docs/knowledge_base", filename);
31
+ if (existsSync(distPath)) {
32
+ return distPath;
33
+ }
34
+ throw new Error(
35
+ `Knowledge base file not found: ${filename}
36
+ Expected at: ${distPath}
37
+ Please run 'npm run build' first to generate the knowledge base files.`
38
+ );
39
+ }
40
+ const fallbackPath = join(currentDir, "docs/knowledge_base", filename);
41
+ if (existsSync(fallbackPath)) {
42
+ return fallbackPath;
43
+ }
44
+ throw new Error(
45
+ `Knowledge base file not found: ${filename}
46
+ Searched in: ${join(currentDir, "docs/knowledge_base", filename)}`
47
+ );
48
+ }
24
49
  function showTools() {
25
50
  const toolNames = tools.map((tool) => tool.name).sort();
26
51
  console.log(toolNames.join("\n"));
@@ -51,8 +76,7 @@ DESCRIPTION:
51
76
  OPTIONS:
52
77
  --url <URL> DocRouter API base URL
53
78
  (default: https://app.docrouter.ai/fastapi)
54
- --org-id <ID> DocRouter organization ID
55
- --org-token <TOKEN> DocRouter organization API token
79
+ --org-token <TOKEN> DocRouter organization API token (required)
56
80
  --timeout <MS> Request timeout in milliseconds (default: 30000)
57
81
  --retries <COUNT> Number of retry attempts (default: 3)
58
82
  --tools List all supported MCP tools
@@ -61,24 +85,22 @@ OPTIONS:
61
85
 
62
86
  ENVIRONMENT VARIABLES:
63
87
  DOCROUTER_API_URL DocRouter API base URL
64
- DOCROUTER_ORG_ID DocRouter organization ID
65
- DOCROUTER_ORG_API_TOKEN DocRouter organization API token
88
+ DOCROUTER_ORG_API_TOKEN DocRouter organization API token (required)
66
89
 
67
90
  EXAMPLES:
68
- # Using command line arguments
69
- docrouter-mcp --org-id "org123" --org-token "token456"
91
+ # Using command line arguments (organization ID will be resolved from token)
92
+ docrouter-mcp --org-token "token456"
70
93
 
71
- # Using environment variables
72
- export DOCROUTER_ORG_ID="org123"
94
+ # Using environment variables (organization ID will be resolved from token)
73
95
  export DOCROUTER_ORG_API_TOKEN="token456"
74
96
  docrouter-mcp
75
97
 
76
98
  # With custom API URL
77
- docrouter-mcp --url "https://custom.docrouter.ai/fastapi" --org-id "org123" --org-token "token456"
99
+ docrouter-mcp --url "https://custom.docrouter.ai/fastapi" --org-token "token456"
78
100
 
79
101
  REQUIRED:
80
- Either provide --org-id and --org-token as command line arguments,
81
- or set DOCROUTER_ORG_ID and DOCROUTER_ORG_API_TOKEN environment variables.
102
+ DOCROUTER_ORG_API_TOKEN environment variable or --org-token argument.
103
+ Organization ID will be automatically resolved from the token.
82
104
 
83
105
  For more information about DocRouter, visit: https://docrouter.ai
84
106
  `);
@@ -106,9 +128,6 @@ function parseConfig() {
106
128
  case "url":
107
129
  config.baseURL = value;
108
130
  break;
109
- case "org-id":
110
- config.organizationId = value;
111
- break;
112
131
  case "org-token":
113
132
  config.orgToken = value;
114
133
  break;
@@ -122,7 +141,6 @@ function parseConfig() {
122
141
  }
123
142
  }
124
143
  config.baseURL = config.baseURL || process.env.DOCROUTER_API_URL || "https://app.docrouter.ai/fastapi";
125
- config.organizationId = config.organizationId || process.env.DOCROUTER_ORG_ID || "";
126
144
  config.orgToken = config.orgToken || process.env.DOCROUTER_ORG_API_TOKEN || "";
127
145
  return ConfigSchema.parse(config);
128
146
  }
@@ -138,20 +156,60 @@ var server = new Server(
138
156
  }
139
157
  );
140
158
  var docrouterClient;
141
- function initializeClient(config) {
142
- docrouterClient = new DocRouterOrg({
143
- baseURL: config.baseURL,
144
- orgToken: config.orgToken,
145
- organizationId: config.organizationId,
146
- timeout: config.timeout,
147
- retries: config.retries
148
- });
159
+ var docrouterAccountClient;
160
+ var orgToken;
161
+ async function initializeClient(config) {
162
+ try {
163
+ console.error("Resolving organization ID from token...");
164
+ docrouterAccountClient = new DocRouterAccount({
165
+ baseURL: config.baseURL,
166
+ accountToken: config.orgToken,
167
+ timeout: config.timeout,
168
+ retries: config.retries
169
+ });
170
+ orgToken = config.orgToken;
171
+ const tokenResponse = await docrouterAccountClient.getOrganizationFromToken(config.orgToken);
172
+ const organizationId = tokenResponse.organization_id;
173
+ if (!organizationId) {
174
+ throw new Error("Token is an account-level token, not an organization-specific token. Please use an organization API token.");
175
+ }
176
+ console.error(`Resolved organization ID: ${organizationId}`);
177
+ docrouterClient = new DocRouterOrg({
178
+ baseURL: config.baseURL,
179
+ orgToken: config.orgToken,
180
+ organizationId,
181
+ timeout: config.timeout,
182
+ retries: config.retries
183
+ });
184
+ } catch (error) {
185
+ throw new Error(`Failed to resolve organization ID from token: ${error instanceof Error ? error.message : "Unknown error"}`);
186
+ }
149
187
  }
150
188
  function handleError(error) {
151
189
  if (error instanceof Error) {
152
- return JSON.stringify({ error: error.message }, null, 2);
190
+ const apiError = error;
191
+ const errorObj = {
192
+ error: error.message
193
+ };
194
+ if (apiError.status !== void 0) {
195
+ errorObj.status = apiError.status;
196
+ }
197
+ if (apiError.code !== void 0) {
198
+ errorObj.code = apiError.code;
199
+ }
200
+ if (apiError.details !== void 0) {
201
+ errorObj.details = apiError.details;
202
+ }
203
+ return JSON.stringify(errorObj, null, 2);
153
204
  }
154
- return JSON.stringify({ error: "Unknown error occurred" }, null, 2);
205
+ if (typeof error === "object" && error !== null) {
206
+ try {
207
+ return JSON.stringify({ error: "Request failed", details: error }, null, 2);
208
+ } catch {
209
+ return JSON.stringify({ error: "Unknown error occurred (could not serialize)" }, null, 2);
210
+ }
211
+ }
212
+ return JSON.stringify({ error: String(error) || "Unknown error occurred" }, null, 2);
155
213
  }
156
214
  function serializeDates(obj) {
157
215
  if (obj === null || obj === void 0) {
@@ -507,7 +565,7 @@ var tools = [
507
565
  // ========== DOCUMENTS ==========
508
566
  {
509
567
  name: "upload_documents",
510
- description: "Upload documents to DocRouter",
568
+ description: "Upload documents to DocRouter from file paths",
511
569
  inputSchema: {
512
570
  type: "object",
513
571
  properties: {
@@ -517,12 +575,12 @@ var tools = [
517
575
  items: {
518
576
  type: "object",
519
577
  properties: {
520
- name: { type: "string", description: "Document name" },
521
- content: { type: "string", description: "Base64 encoded document content (supports both plain base64 and data URLs)" },
578
+ file_path: { type: "string", description: "Path to the document file on disk" },
579
+ name: { type: "string", description: "Document name (optional, defaults to filename)" },
522
580
  tag_ids: { type: "array", items: { type: "string" }, description: "Optional list of tag IDs" },
523
581
  metadata: { type: "object", description: "Optional metadata" }
524
582
  },
525
- required: ["name", "content"]
583
+ required: ["file_path"]
526
584
  }
527
585
  }
528
586
  },
@@ -545,12 +603,13 @@ var tools = [
545
603
  },
546
604
  {
547
605
  name: "get_document",
548
- description: "Get document by ID from DocRouter",
606
+ description: "Get document metadata (state, tags, metadata) and optionally download the file to disk",
549
607
  inputSchema: {
550
608
  type: "object",
551
609
  properties: {
552
610
  documentId: { type: "string", description: "ID of the document to retrieve" },
553
- fileType: { type: "string", description: "File type to retrieve (pdf, image, etc.)", default: "pdf" }
611
+ fileType: { type: "string", description: "File type to retrieve (original or pdf)", default: "original" },
612
+ save_path: { type: "string", description: "Optional file path or directory to save the document. If directory, uses original filename. If not provided, file is not downloaded." }
554
613
  },
555
614
  required: ["documentId"]
556
615
  }
@@ -887,9 +946,12 @@ var tools = [
887
946
  type: "object",
888
947
  properties: {
889
948
  promptId: { type: "string", description: "ID of the prompt" },
890
- prompt: { type: "object", description: "Prompt data" }
949
+ content: { type: "string", description: "Prompt content" },
950
+ model: { type: "string", description: "LLM model to use" },
951
+ schema_id: { type: "string", description: "Schema ID to link" },
952
+ tag_ids: { type: "array", items: { type: "string" }, description: "Tag IDs that trigger this prompt" }
891
953
  },
892
- required: ["promptId", "prompt"]
954
+ required: ["promptId"]
893
955
  }
894
956
  },
895
957
  {
@@ -1023,6 +1085,22 @@ var tools = [
1023
1085
  required: ["messages", "model"]
1024
1086
  }
1025
1087
  },
1088
+ {
1089
+ name: "get_organization",
1090
+ description: "Get information about the current organization (name, type, ID)",
1091
+ inputSchema: {
1092
+ type: "object",
1093
+ properties: {}
1094
+ }
1095
+ },
1096
+ {
1097
+ name: "list_llm_models",
1098
+ description: "List enabled LLM models available for use in prompts for this organization",
1099
+ inputSchema: {
1100
+ type: "object",
1101
+ properties: {}
1102
+ }
1103
+ },
1026
1104
  // ========== HELPER TOOLS ==========
1027
1105
  {
1028
1106
  name: "help",
@@ -1084,21 +1162,57 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1084
1162
  };
1085
1163
  }
1086
1164
  case "get_document": {
1087
- const result = await docrouterClient.getDocument({
1088
- documentId: getArg(args, "documentId"),
1089
- fileType: getArg(args, "fileType", "pdf")
1165
+ const documentId = getArg(args, "documentId");
1166
+ const fileType = getArg(args, "fileType", "original");
1167
+ const savePath = getOptionalArg(args, "save_path");
1168
+ const fileResult = await docrouterClient.getDocument({
1169
+ documentId,
1170
+ fileType
1090
1171
  });
1091
- const base64Content = Buffer.from(result.content).toString("base64");
1172
+ const response = {
1173
+ id: fileResult.id,
1174
+ pdf_id: fileResult.pdf_id,
1175
+ document_name: fileResult.document_name,
1176
+ upload_date: fileResult.upload_date,
1177
+ uploaded_by: fileResult.uploaded_by,
1178
+ state: fileResult.state,
1179
+ tag_ids: fileResult.tag_ids,
1180
+ type: fileResult.type,
1181
+ metadata: fileResult.metadata
1182
+ };
1183
+ if (savePath) {
1184
+ let finalPath;
1185
+ if (isAbsolute(savePath)) {
1186
+ finalPath = savePath;
1187
+ } else {
1188
+ finalPath = resolve(process.cwd(), savePath);
1189
+ }
1190
+ let isDirectory = false;
1191
+ try {
1192
+ const stats = statSync(finalPath);
1193
+ isDirectory = stats.isDirectory();
1194
+ } catch {
1195
+ isDirectory = savePath.endsWith("/") || savePath.endsWith("\\");
1196
+ }
1197
+ if (isDirectory) {
1198
+ const extension = fileType === "pdf" ? ".pdf" : extname(fileResult.document_name) || ".pdf";
1199
+ const fileName = basename(fileResult.document_name, extname(fileResult.document_name)) + extension;
1200
+ finalPath = join(finalPath, fileName);
1201
+ }
1202
+ const targetDir = dirname(finalPath);
1203
+ if (!existsSync(targetDir)) {
1204
+ mkdirSync(targetDir, { recursive: true });
1205
+ }
1206
+ const fileBuffer = Buffer.from(fileResult.content);
1207
+ writeFileSync(finalPath, fileBuffer);
1208
+ response.saved_path = finalPath;
1209
+ response.file_size = fileBuffer.length;
1210
+ }
1092
1211
  return {
1093
1212
  content: [
1094
1213
  {
1095
1214
  type: "text",
1096
- text: JSON.stringify({
1097
- ...serializeDates(result),
1098
- content: base64Content,
1099
- content_type: "base64",
1100
- content_size: result.content.byteLength
1101
- }, null, 2)
1215
+ text: JSON.stringify(response, null, 2)
1102
1216
  }
1103
1217
  ]
1104
1218
  };
@@ -1221,7 +1335,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1221
1335
  // ========== DOCUMENTS ==========
1222
1336
  case "upload_documents": {
1223
1337
  const documentsInput = getArg(args, "documents");
1224
- const result = await docrouterClient.uploadDocuments({ documents: documentsInput });
1338
+ const documents = [];
1339
+ for (const doc of documentsInput) {
1340
+ let filePath;
1341
+ if (isAbsolute(doc.file_path)) {
1342
+ filePath = doc.file_path;
1343
+ } else {
1344
+ filePath = resolve(process.cwd(), doc.file_path);
1345
+ }
1346
+ if (!existsSync(filePath)) {
1347
+ throw new Error(`File not found: ${filePath}`);
1348
+ }
1349
+ const fileBuffer = readFileSync(filePath);
1350
+ const base64Content = fileBuffer.toString("base64");
1351
+ const fileName = doc.name || filePath.split(/[/\\]/).pop() || "document";
1352
+ documents.push({
1353
+ name: fileName,
1354
+ content: base64Content,
1355
+ tag_ids: doc.tag_ids,
1356
+ metadata: doc.metadata
1357
+ });
1358
+ }
1359
+ const result = await docrouterClient.uploadDocuments({ documents });
1225
1360
  return {
1226
1361
  content: [
1227
1362
  {
@@ -1475,9 +1610,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1475
1610
  };
1476
1611
  }
1477
1612
  case "update_prompt": {
1613
+ const promptId = getArg(args, "promptId");
1614
+ const content = getOptionalArg(args, "content");
1615
+ const model = getOptionalArg(args, "model");
1616
+ const schema_id = getOptionalArg(args, "schema_id");
1617
+ const tag_ids = getOptionalArg(args, "tag_ids");
1618
+ let currentPrompt = null;
1619
+ const allPrompts = await docrouterClient.listPrompts({});
1620
+ for (const p of allPrompts.prompts) {
1621
+ if (p.prompt_id === promptId) {
1622
+ currentPrompt = p;
1623
+ break;
1624
+ }
1625
+ }
1626
+ if (!currentPrompt) {
1627
+ throw new Error(`Prompt with ID ${promptId} not found`);
1628
+ }
1478
1629
  const result = await docrouterClient.updatePrompt({
1479
- promptId: getArg(args, "promptId"),
1480
- prompt: getArg(args, "prompt")
1630
+ promptId,
1631
+ prompt: {
1632
+ name: currentPrompt.name,
1633
+ content: content ?? currentPrompt.content,
1634
+ schema_id: schema_id ?? currentPrompt.schema_id,
1635
+ schema_version: currentPrompt.schema_version,
1636
+ tag_ids: tag_ids ?? currentPrompt.tag_ids,
1637
+ model: model ?? currentPrompt.model
1638
+ }
1481
1639
  });
1482
1640
  return {
1483
1641
  content: [
@@ -1661,6 +1819,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1661
1819
  ]
1662
1820
  };
1663
1821
  }
1822
+ case "get_organization": {
1823
+ const result = await docrouterAccountClient.getOrganizationFromToken(orgToken);
1824
+ return {
1825
+ content: [
1826
+ {
1827
+ type: "text",
1828
+ text: JSON.stringify(serializeDates(result), null, 2)
1829
+ }
1830
+ ]
1831
+ };
1832
+ }
1833
+ case "list_llm_models": {
1834
+ const result = await docrouterClient.listLLMModels();
1835
+ return {
1836
+ content: [
1837
+ {
1838
+ type: "text",
1839
+ text: JSON.stringify(serializeDates(result), null, 2)
1840
+ }
1841
+ ]
1842
+ };
1843
+ }
1664
1844
  // ========== HELPER TOOLS ==========
1665
1845
  case "help": {
1666
1846
  const helpText = `
@@ -1671,7 +1851,7 @@ This server provides access to DocRouter resources and tools.
1671
1851
  ## Available Tools
1672
1852
 
1673
1853
  ### Documents
1674
- - \`upload_documents(documents)\` - Upload documents
1854
+ - \`upload_documents(documents)\` - Upload documents from file paths
1675
1855
  - \`list_documents(skip, limit, tagIds, nameSearch, metadataSearch)\` - List documents
1676
1856
  - \`get_document(documentId, fileType)\` - Get document by ID
1677
1857
  - \`update_document(documentId, documentName, tagIds, metadata)\` - Update document
@@ -1725,6 +1905,10 @@ This server provides access to DocRouter resources and tools.
1725
1905
  ### LLM Chat
1726
1906
  - \`run_llm_chat(messages, model, temperature, max_tokens, stream)\` - Run chat
1727
1907
 
1908
+ ### Organization
1909
+ - \`get_organization()\` - Get information about the current organization (name, type, ID)
1910
+ - \`list_llm_models()\` - List enabled LLM models available for use in prompts
1911
+
1728
1912
  ### Help Tools
1729
1913
  - \`help()\` - Get general API help information
1730
1914
  - \`help_prompts()\` - Get detailed help on creating and configuring prompts
@@ -1768,9 +1952,7 @@ This server provides access to DocRouter resources and tools.
1768
1952
  }
1769
1953
  case "help_prompts": {
1770
1954
  try {
1771
- const currentFile = fileURLToPath(import.meta.url);
1772
- const currentDir = dirname(currentFile);
1773
- const promptsPath = join(currentDir, "docs/knowledge_base/prompts.md");
1955
+ const promptsPath = resolveKnowledgeBasePath("prompts.md");
1774
1956
  const promptsContent = readFileSync(promptsPath, "utf-8");
1775
1957
  return {
1776
1958
  content: [
@@ -1794,9 +1976,7 @@ This server provides access to DocRouter resources and tools.
1794
1976
  }
1795
1977
  case "help_schemas": {
1796
1978
  try {
1797
- const currentFile = fileURLToPath(import.meta.url);
1798
- const currentDir = dirname(currentFile);
1799
- const schemasPath = join(currentDir, "docs/knowledge_base/schemas.md");
1979
+ const schemasPath = resolveKnowledgeBasePath("schemas.md");
1800
1980
  const schemasContent = readFileSync(schemasPath, "utf-8");
1801
1981
  return {
1802
1982
  content: [
@@ -1820,9 +2000,7 @@ This server provides access to DocRouter resources and tools.
1820
2000
  }
1821
2001
  case "help_forms": {
1822
2002
  try {
1823
- const currentFile = fileURLToPath(import.meta.url);
1824
- const currentDir = dirname(currentFile);
1825
- const formsPath = join(currentDir, "docs/knowledge_base/forms.md");
2003
+ const formsPath = resolveKnowledgeBasePath("forms.md");
1826
2004
  const formsContent = readFileSync(formsPath, "utf-8");
1827
2005
  return {
1828
2006
  content: [
@@ -1862,12 +2040,13 @@ This server provides access to DocRouter resources and tools.
1862
2040
  async function main() {
1863
2041
  try {
1864
2042
  const config = parseConfig();
1865
- if (!config.organizationId || !config.orgToken) {
1866
- console.error("Error: DOCROUTER_ORG_ID and DOCROUTER_ORG_API_TOKEN environment variables are required");
1867
- console.error("Or provide them as command line arguments: --org-id <id> --org-token <token>");
2043
+ if (!config.orgToken) {
2044
+ console.error("Error: DOCROUTER_ORG_API_TOKEN environment variable is required");
2045
+ console.error("Or provide it as command line argument: --org-token <token>");
2046
+ console.error("Organization ID will be automatically resolved from the token.");
1868
2047
  process.exit(1);
1869
2048
  }
1870
- initializeClient(config);
2049
+ await initializeClient(config);
1871
2050
  const transport = new StdioServerTransport();
1872
2051
  await server.connect(transport);
1873
2052
  console.error("DocRouter MCP server started successfully");