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.
- package/build/index.js +76 -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:
|
|
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;
|