secondbrainos-mcp-server 1.2.6 → 1.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.
Files changed (2) hide show
  1. package/build/index.js +76 -2
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -5,6 +5,8 @@ import { OpenApi, HttpLlm } from "@samchon/openapi";
5
5
  import axios from "axios";
6
6
  import dotenv from "dotenv";
7
7
  import yaml from 'js-yaml';
8
+ import fs from 'fs';
9
+ import path from 'path';
8
10
  dotenv.config();
9
11
  function toSnakeCase(str) {
10
12
  return str
@@ -43,6 +45,7 @@ class SecondBrainOSServer {
43
45
  // Discover service paths from the OpenAPI schema
44
46
  this.runPromptChainPath = null;
45
47
  this.getAIAgentsSchemaPath = null;
48
+ this.generateUploadURLPath = null;
46
49
  if (initialSchema.paths) {
47
50
  for (const [path, pathItem] of Object.entries(initialSchema.paths)) {
48
51
  for (const operation of Object.values(pathItem)) {
@@ -52,8 +55,11 @@ class SecondBrainOSServer {
52
55
  if (operation.operationId === 'getAIAgentsSchema') {
53
56
  this.getAIAgentsSchemaPath = path;
54
57
  }
58
+ if (operation.operationId === 'generateFileUploadGoogleCloudStorageURL') {
59
+ this.generateUploadURLPath = path;
60
+ }
55
61
  }
56
- if (this.runPromptChainPath && this.getAIAgentsSchemaPath)
62
+ if (this.runPromptChainPath && this.getAIAgentsSchemaPath && this.generateUploadURLPath)
57
63
  break;
58
64
  }
59
65
  }
@@ -131,6 +137,8 @@ class SecondBrainOSServer {
131
137
  throw new McpError(ErrorCode.MethodNotFound, `Unknown function: ${request.params.name}`);
132
138
  }
133
139
  try {
140
+ // Intercept file_path arguments: upload local files to GCS
141
+ const processedArgs = await this.interceptFilePathArgs((request.params.arguments || {}));
134
142
  // Use HttpLlm.execute for better error handling and type safety
135
143
  const result = await HttpLlm.execute({
136
144
  connection: {
@@ -141,7 +149,7 @@ class SecondBrainOSServer {
141
149
  },
142
150
  application: this.application, // Type assertion to avoid generic issues
143
151
  function: func,
144
- input: request.params.arguments || {}
152
+ input: processedArgs
145
153
  });
146
154
  return {
147
155
  content: [{
@@ -456,6 +464,71 @@ class SecondBrainOSServer {
456
464
  });
457
465
  return response.data;
458
466
  }
467
+ /**
468
+ * Scans tool arguments for file_path parameters containing local paths,
469
+ * uploads those files to GCS via signed URL, and replaces the values with GCS paths.
470
+ * - https:// paths are passed through
471
+ * - http:// paths are rejected
472
+ * - Everything else is treated as a local file path
473
+ */
474
+ async interceptFilePathArgs(args) {
475
+ const result = { ...args };
476
+ for (const [key, value] of Object.entries(result)) {
477
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
478
+ result[key] = await this.interceptFilePathArgs(value);
479
+ continue;
480
+ }
481
+ if (typeof value !== 'string' || !key.toLowerCase().includes('file_path')) {
482
+ continue;
483
+ }
484
+ const trimmed = value.trim();
485
+ // https:// — pass through
486
+ if (trimmed.startsWith('https://'))
487
+ continue;
488
+ // http:// — reject
489
+ if (trimmed.startsWith('http://')) {
490
+ throw new McpError(ErrorCode.InvalidRequest, `Insecure http:// URLs are not allowed for file_path. Use https:// or a local file path.`);
491
+ }
492
+ // Local file path — validate, upload via signed URL, replace
493
+ if (!this.generateUploadURLPath) {
494
+ throw new McpError(ErrorCode.InternalError, 'File upload service (generateFileUploadGoogleCloudStorageURL) is not available for this user. Cannot upload local files.');
495
+ }
496
+ const ext = path.extname(trimmed).toLowerCase();
497
+ if (!SecondBrainOSServer.ALLOWED_EXTENSIONS.includes(ext)) {
498
+ throw new McpError(ErrorCode.InvalidRequest, `Unsupported file extension "${ext}" for file_path. Only ${SecondBrainOSServer.ALLOWED_EXTENSIONS.join(', ')} are supported.`);
499
+ }
500
+ if (!fs.existsSync(trimmed)) {
501
+ throw new McpError(ErrorCode.InvalidRequest, `Local file not found: ${trimmed}`);
502
+ }
503
+ const fileContent = fs.readFileSync(trimmed, 'utf-8');
504
+ if (!fileContent.trim()) {
505
+ throw new McpError(ErrorCode.InvalidRequest, `File is empty: ${trimmed}`);
506
+ }
507
+ const fileName = path.basename(trimmed);
508
+ const fileSize = Buffer.byteLength(fileContent, 'utf-8');
509
+ // Step 1: Get signed upload URL from the GCF
510
+ const uploadUrlResponse = await axios.post(`${this.baseUrl}${this.generateUploadURLPath}`, {
511
+ file_name: fileName,
512
+ file_size: fileSize,
513
+ mime_type: 'text/plain'
514
+ }, {
515
+ headers: {
516
+ 'Authorization': `Bearer ${this.userId}:${this.userSecret}`,
517
+ 'Content-Type': 'application/json'
518
+ }
519
+ });
520
+ const { upload_url, gs_path } = uploadUrlResponse.data;
521
+ // Step 2: PUT file content directly to GCS via signed URL
522
+ await axios.put(upload_url, fileContent, {
523
+ headers: {
524
+ 'Content-Type': 'text/plain'
525
+ }
526
+ });
527
+ console.error(`Uploaded local file to ${gs_path}`);
528
+ result[key] = gs_path;
529
+ }
530
+ return result;
531
+ }
459
532
  async fetchAndEnrichAgents() {
460
533
  if (this.cachedAgents)
461
534
  return this.cachedAgents;
@@ -499,6 +572,7 @@ class SecondBrainOSServer {
499
572
  console.error("Second Brain OS MCP server running on stdio");
500
573
  }
501
574
  }
575
+ SecondBrainOSServer.ALLOWED_EXTENSIONS = ['.txt', '.md'];
502
576
  // Function to fetch the schema from the API
503
577
  async function fetchSchema() {
504
578
  const userId = process.env.USER_ID;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secondbrainos-mcp-server",
3
- "version": "1.2.6",
3
+ "version": "1.3.0",
4
4
  "description": "Second Brain OS MCP Server for Claude Desktop",
5
5
  "type": "module",
6
6
  "main": "build/index.js",