secondbrainos-mcp-server 1.0.5 → 1.0.8
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/bin/cli.js +42 -15
- package/build/index.js +36 -9
- package/build/test-mcp-conversion.js +135 -0
- package/package.json +3 -3
- package/build/test-conversion.js +0 -81
package/bin/cli.js
CHANGED
|
@@ -116,6 +116,19 @@ function getNodeModulesPath() {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Read existing Claude Desktop configuration
|
|
121
|
+
*/
|
|
122
|
+
async function readExistingConfig(configPath) {
|
|
123
|
+
try {
|
|
124
|
+
const configData = await fs.readFile(configPath, 'utf8');
|
|
125
|
+
return JSON.parse(configData);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// If file doesn't exist or is invalid JSON, return empty config
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
async function main() {
|
|
120
133
|
try {
|
|
121
134
|
// Get command line arguments
|
|
@@ -208,25 +221,39 @@ async function main() {
|
|
|
208
221
|
// Find Node.js executable
|
|
209
222
|
const nodePath = await findNodeExecutable();
|
|
210
223
|
|
|
211
|
-
//
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
// Read existing configuration
|
|
225
|
+
const existingConfig = await readExistingConfig(claudeConfigFile);
|
|
226
|
+
|
|
227
|
+
// Ensure mcpServers object exists
|
|
228
|
+
if (!existingConfig.mcpServers) {
|
|
229
|
+
existingConfig.mcpServers = {};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check if secondbrainos already exists
|
|
233
|
+
const hadExistingSecondBrainOS = !!existingConfig.mcpServers.secondbrainos;
|
|
234
|
+
|
|
235
|
+
// Add or update the secondbrainos server configuration
|
|
236
|
+
existingConfig.mcpServers.secondbrainos = {
|
|
237
|
+
command: nodePath,
|
|
238
|
+
args: [serverPath],
|
|
239
|
+
env: {
|
|
240
|
+
USER_ID,
|
|
241
|
+
USER_SECRET,
|
|
242
|
+
API_BASE_URL: 'https://api.secondbrainos.com',
|
|
243
|
+
NODE_PATH: getNodeModulesPath()
|
|
224
244
|
}
|
|
225
245
|
};
|
|
226
246
|
|
|
227
247
|
try {
|
|
228
|
-
|
|
229
|
-
|
|
248
|
+
// Write the updated configuration back
|
|
249
|
+
await fs.writeFile(claudeConfigFile, JSON.stringify(existingConfig, null, 2));
|
|
250
|
+
console.log(`Configuration file ${hadExistingSecondBrainOS ? 'updated' : 'created'} at: ${claudeConfigFile}`);
|
|
251
|
+
|
|
252
|
+
// Show summary of configured servers
|
|
253
|
+
const serverCount = Object.keys(existingConfig.mcpServers).length;
|
|
254
|
+
console.log(`\nTotal MCP servers configured: ${serverCount}`);
|
|
255
|
+
console.log('Configured servers:', Object.keys(existingConfig.mcpServers).join(', '));
|
|
256
|
+
|
|
230
257
|
} catch (error) {
|
|
231
258
|
console.error('Error writing configuration file:', error);
|
|
232
259
|
process.exit(1);
|
package/build/index.js
CHANGED
|
@@ -22,6 +22,14 @@ class SecondBrainOSServer {
|
|
|
22
22
|
model: "chatgpt",
|
|
23
23
|
document
|
|
24
24
|
});
|
|
25
|
+
// Create a map of operationId to function for easier lookup
|
|
26
|
+
this.functionMap = new Map();
|
|
27
|
+
this.application.functions.forEach(func => {
|
|
28
|
+
const operation = func.operation();
|
|
29
|
+
if (operation.operationId) {
|
|
30
|
+
this.functionMap.set(operation.operationId, func);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
25
33
|
this.server = new Server({
|
|
26
34
|
name: "secondbrainos-server",
|
|
27
35
|
version: "1.0.0"
|
|
@@ -59,19 +67,38 @@ class SecondBrainOSServer {
|
|
|
59
67
|
}]
|
|
60
68
|
};
|
|
61
69
|
});
|
|
62
|
-
// List available tools - now using
|
|
70
|
+
// List available tools - now using operationId as the tool name
|
|
63
71
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
const tools = [];
|
|
73
|
+
// Iterate through the function map to get tools with proper names
|
|
74
|
+
for (const [operationId, func] of this.functionMap) {
|
|
75
|
+
tools.push({
|
|
76
|
+
name: operationId,
|
|
77
|
+
description: func.description || operationId,
|
|
68
78
|
inputSchema: func.parameters // Convert to MCP format
|
|
69
|
-
})
|
|
70
|
-
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Also include functions without operationId (fallback)
|
|
82
|
+
this.application.functions.forEach(func => {
|
|
83
|
+
const operation = func.operation();
|
|
84
|
+
if (!operation.operationId) {
|
|
85
|
+
tools.push({
|
|
86
|
+
name: func.name,
|
|
87
|
+
description: func.description || func.name,
|
|
88
|
+
inputSchema: func.parameters
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return { tools };
|
|
71
93
|
});
|
|
72
|
-
// Handle tool calls
|
|
94
|
+
// Handle tool calls - now supporting both operationId and original names
|
|
73
95
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
74
|
-
|
|
96
|
+
// First try to find by operationId
|
|
97
|
+
let func = this.functionMap.get(request.params.name);
|
|
98
|
+
// If not found, try the original function name
|
|
99
|
+
if (!func) {
|
|
100
|
+
func = this.application.functions.find(f => f.name === request.params.name);
|
|
101
|
+
}
|
|
75
102
|
if (!func) {
|
|
76
103
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown function: ${request.params.name}`);
|
|
77
104
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { OpenApi, HttpLlm } from "@samchon/openapi";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
import fs from "fs/promises";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { dirname } from 'path';
|
|
8
|
+
// Get __dirname equivalent for ES modules
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
// Load environment variables
|
|
12
|
+
dotenv.config();
|
|
13
|
+
// Function to fetch the schema from the API
|
|
14
|
+
async function fetchSchema() {
|
|
15
|
+
const userId = process.env.USER_ID;
|
|
16
|
+
if (!userId) {
|
|
17
|
+
throw new Error("USER_ID environment variable is required");
|
|
18
|
+
}
|
|
19
|
+
console.log("Fetching schema for user:", userId);
|
|
20
|
+
try {
|
|
21
|
+
const response = await axios.post('https://us-central1-second-brain-os.cloudfunctions.net/generate-open-api-schema', { user_id: userId }, {
|
|
22
|
+
headers: {
|
|
23
|
+
'Content-Type': 'application/json'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return response.data;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Failed to fetch schema:', error);
|
|
30
|
+
throw new Error('Failed to fetch API schema');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function testMCPConversion() {
|
|
34
|
+
try {
|
|
35
|
+
console.log("=== MCP Conversion Test ===\n");
|
|
36
|
+
// Step 1: Fetch the original schema
|
|
37
|
+
console.log("1. Fetching OpenAPI schema...");
|
|
38
|
+
const originalSchema = await fetchSchema();
|
|
39
|
+
console.log("✓ Schema fetched successfully");
|
|
40
|
+
console.log(` - Title: ${originalSchema.info?.title}`);
|
|
41
|
+
console.log(` - Version: ${originalSchema.info?.version}`);
|
|
42
|
+
console.log(` - Number of paths: ${Object.keys(originalSchema.paths || {}).length}\n`);
|
|
43
|
+
// Save original schema for reference
|
|
44
|
+
await fs.writeFile(path.join(__dirname, '../output/original-schema.json'), JSON.stringify(originalSchema, null, 2));
|
|
45
|
+
console.log("✓ Original schema saved to output/original-schema.json\n");
|
|
46
|
+
// Step 2: Convert using OpenApi
|
|
47
|
+
console.log("2. Converting schema using @samchon/openapi...");
|
|
48
|
+
const document = OpenApi.convert(originalSchema);
|
|
49
|
+
console.log("✓ Schema converted to OpenApi format\n");
|
|
50
|
+
// Step 3: Create LLM application
|
|
51
|
+
console.log("3. Creating LLM application...");
|
|
52
|
+
const application = HttpLlm.application({
|
|
53
|
+
model: "chatgpt",
|
|
54
|
+
document
|
|
55
|
+
});
|
|
56
|
+
console.log("✓ LLM application created");
|
|
57
|
+
console.log(` - Number of functions: ${application.functions.length}\n`);
|
|
58
|
+
// Step 4: Create MCP-compatible tools
|
|
59
|
+
console.log("4. Converting to MCP tool format...");
|
|
60
|
+
const mcpTools = [];
|
|
61
|
+
const functionMap = new Map();
|
|
62
|
+
application.functions.forEach(func => {
|
|
63
|
+
const operation = func.operation();
|
|
64
|
+
const operationId = operation.operationId || func.name;
|
|
65
|
+
functionMap.set(operationId, func);
|
|
66
|
+
const mcpTool = {
|
|
67
|
+
name: operationId,
|
|
68
|
+
description: func.description || operationId,
|
|
69
|
+
inputSchema: func.parameters
|
|
70
|
+
};
|
|
71
|
+
mcpTools.push(mcpTool);
|
|
72
|
+
});
|
|
73
|
+
console.log(`✓ Converted ${mcpTools.length} tools to MCP format\n`);
|
|
74
|
+
// Step 5: Save the results
|
|
75
|
+
console.log("5. Saving conversion results...");
|
|
76
|
+
// Create output directory if it doesn't exist
|
|
77
|
+
await fs.mkdir(path.join(__dirname, '../output'), { recursive: true });
|
|
78
|
+
// Save MCP tools
|
|
79
|
+
await fs.writeFile(path.join(__dirname, '../output/mcp-tools.json'), JSON.stringify(mcpTools, null, 2));
|
|
80
|
+
console.log("✓ MCP tools saved to output/mcp-tools.json");
|
|
81
|
+
// Save function details
|
|
82
|
+
const functionDetails = application.functions.map(func => {
|
|
83
|
+
const operation = func.operation();
|
|
84
|
+
// Extract method and path from the route property if available
|
|
85
|
+
let method;
|
|
86
|
+
let path;
|
|
87
|
+
// The operation might have these properties in different ways depending on the OpenAPI version
|
|
88
|
+
// Let's check the actual structure
|
|
89
|
+
const route = operation.route;
|
|
90
|
+
if (route) {
|
|
91
|
+
method = route.method;
|
|
92
|
+
path = route.path;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
name: func.name,
|
|
96
|
+
operationId: operation.operationId,
|
|
97
|
+
method,
|
|
98
|
+
path,
|
|
99
|
+
description: func.description,
|
|
100
|
+
parameters: func.parameters,
|
|
101
|
+
operation: {
|
|
102
|
+
summary: operation.summary,
|
|
103
|
+
description: operation.description,
|
|
104
|
+
tags: operation.tags
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
await fs.writeFile(path.join(__dirname, '../output/function-details.json'), JSON.stringify(functionDetails, null, 2));
|
|
109
|
+
console.log("✓ Function details saved to output/function-details.json\n");
|
|
110
|
+
// Step 6: Display summary
|
|
111
|
+
console.log("=== Conversion Summary ===");
|
|
112
|
+
console.log(`Total tools converted: ${mcpTools.length}`);
|
|
113
|
+
console.log("\nSample tools:");
|
|
114
|
+
mcpTools.slice(0, 5).forEach(tool => {
|
|
115
|
+
console.log(` - ${tool.name}: ${tool.description}`);
|
|
116
|
+
});
|
|
117
|
+
if (mcpTools.length > 5) {
|
|
118
|
+
console.log(` ... and ${mcpTools.length - 5} more`);
|
|
119
|
+
}
|
|
120
|
+
console.log("\n✓ Test completed successfully!");
|
|
121
|
+
console.log("\nCheck the 'output' directory for:");
|
|
122
|
+
console.log(" - original-schema.json: The raw OpenAPI schema from the API");
|
|
123
|
+
console.log(" - mcp-tools.json: Tools in MCP format");
|
|
124
|
+
console.log(" - function-details.json: Detailed function information");
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error("\n❌ Test failed:", error);
|
|
128
|
+
if (error instanceof Error) {
|
|
129
|
+
console.error("Error details:", error.message);
|
|
130
|
+
}
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Run the test
|
|
135
|
+
testMCPConversion().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "secondbrainos-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Second Brain OS MCP Server for Claude Desktop",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"dev": "tsx src/index.ts",
|
|
20
20
|
"start": "node build/index.js",
|
|
21
|
-
"test-conversion": "tsx src/test-conversion.ts",
|
|
21
|
+
"test-conversion": "tsx src/test-mcp-conversion.ts",
|
|
22
22
|
"prepublishOnly": "npm run build",
|
|
23
23
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
24
24
|
},
|
|
@@ -47,4 +47,4 @@
|
|
|
47
47
|
"engines": {
|
|
48
48
|
"node": ">=16.0.0"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
package/build/test-conversion.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { OpenApi, HttpLlm } from "@samchon/openapi";
|
|
2
|
-
import axios from "axios";
|
|
3
|
-
import dotenv from "dotenv";
|
|
4
|
-
dotenv.config();
|
|
5
|
-
async function testSchemaConversion() {
|
|
6
|
-
const userId = process.env.USER_ID;
|
|
7
|
-
const userSecret = process.env.USER_SECRET;
|
|
8
|
-
if (!userId || !userSecret) {
|
|
9
|
-
console.error("❌ USER_ID and USER_SECRET environment variables are required");
|
|
10
|
-
console.error("Create a .env file with:");
|
|
11
|
-
console.error("USER_ID=your_user_id");
|
|
12
|
-
console.error("USER_SECRET=your_user_secret");
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
console.log("🔄 Fetching OpenAPI schema from Second Brain OS...");
|
|
16
|
-
try {
|
|
17
|
-
// Fetch the schema
|
|
18
|
-
const response = await axios.post('https://us-central1-second-brain-os.cloudfunctions.net/generate-open-api-schema', { user_id: userId }, {
|
|
19
|
-
headers: {
|
|
20
|
-
'Content-Type': 'application/json'
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
const openApiSchema = response.data;
|
|
24
|
-
console.log("✅ Schema fetched successfully!");
|
|
25
|
-
console.log(`📋 API Title: ${openApiSchema.info?.title || 'Unknown'}`);
|
|
26
|
-
console.log(`📋 API Version: ${openApiSchema.info?.version || 'Unknown'}`);
|
|
27
|
-
// Convert to emended OpenAPI document
|
|
28
|
-
console.log("\n🔄 Converting to emended OpenAPI format...");
|
|
29
|
-
const document = OpenApi.convert(openApiSchema);
|
|
30
|
-
console.log("✅ Conversion successful!");
|
|
31
|
-
// Create LLM application
|
|
32
|
-
console.log("\n🔄 Creating LLM function calling application...");
|
|
33
|
-
const application = HttpLlm.application({
|
|
34
|
-
model: "chatgpt",
|
|
35
|
-
document
|
|
36
|
-
});
|
|
37
|
-
console.log("✅ LLM application created!");
|
|
38
|
-
// Display the converted functions
|
|
39
|
-
console.log(`\n📊 Total functions available: ${application.functions.length}`);
|
|
40
|
-
console.log(`⚠️ Errors during conversion: ${application.errors.length}`);
|
|
41
|
-
if (application.errors.length > 0) {
|
|
42
|
-
console.log("\n❌ Conversion errors:");
|
|
43
|
-
application.errors.forEach((error, index) => {
|
|
44
|
-
console.log(` ${index + 1}. ${error.method.toUpperCase()} ${error.path}`);
|
|
45
|
-
error.messages.forEach(msg => console.log(` - ${msg}`));
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
console.log("\n🔧 Available MCP Tools:");
|
|
49
|
-
console.log("=".repeat(80));
|
|
50
|
-
application.functions.slice(0, 10).forEach((func, index) => {
|
|
51
|
-
console.log(`\n${index + 1}. ${func.name}`);
|
|
52
|
-
console.log(` Method: ${func.method.toUpperCase()}`);
|
|
53
|
-
console.log(` Path: ${func.path}`);
|
|
54
|
-
console.log(` Description: ${func.description?.split('\n')[0] || 'No description'}`);
|
|
55
|
-
console.log(` Parameters Schema: ${JSON.stringify(func.parameters).substring(0, 100)}...`);
|
|
56
|
-
});
|
|
57
|
-
if (application.functions.length > 10) {
|
|
58
|
-
console.log(`\n... and ${application.functions.length - 10} more functions`);
|
|
59
|
-
}
|
|
60
|
-
// Show a sample of how it would look as MCP tools
|
|
61
|
-
console.log("\n\n🎯 Sample MCP Tool Format:");
|
|
62
|
-
console.log("=".repeat(80));
|
|
63
|
-
const sampleFunc = application.functions[0];
|
|
64
|
-
if (sampleFunc) {
|
|
65
|
-
const mcpTool = {
|
|
66
|
-
name: sampleFunc.name,
|
|
67
|
-
description: sampleFunc.description || sampleFunc.name,
|
|
68
|
-
inputSchema: sampleFunc.parameters
|
|
69
|
-
};
|
|
70
|
-
console.log(JSON.stringify(mcpTool, null, 2));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
console.error("❌ Error:", error instanceof Error ? error.message : error);
|
|
75
|
-
if (axios.isAxiosError(error) && error.response) {
|
|
76
|
-
console.error("Response data:", error.response.data);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// Run the test
|
|
81
|
-
testSchemaConversion();
|