secondbrainos-mcp-server 1.0.4 → 1.0.6

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/README.md CHANGED
@@ -28,25 +28,122 @@ This will:
28
28
  3. Create the necessary configuration files for Claude Desktop
29
29
  4. Configure the server to dynamically fetch your available API endpoints
30
30
 
31
- ### After Installation
31
+ ## Features
32
32
 
33
- 1. Completely quit Claude Desktop if it is running
34
- 2. Restart Claude Desktop
35
- 3. Look for the Second Brain OS server in the 🔌 plugins menu
33
+ ### Enhanced OpenAPI Support
34
+ The server uses `@samchon/openapi` library for robust OpenAPI handling, providing:
35
+ - Support for all OpenAPI/Swagger versions (v2.0, v3.0, v3.1)
36
+ - Automatic schema conversion to a standardized format
37
+ - Better handling of complex schemas including references, nullable properties, and union types
36
38
 
37
- ## Troubleshooting
39
+ ### Improved Function Execution
40
+ - Type-safe function execution with built-in error handling
41
+ - Automatic request formatting based on OpenAPI specifications
42
+ - Support for complex parameters and nested objects
43
+
44
+ ### Better Error Handling
45
+ - Detailed error messages for debugging
46
+ - Proper handling of authentication failures, bad requests, and service errors
47
+ - Graceful fallback for unexpected errors
48
+
49
+ ## Development
50
+
51
+ ### Setup
52
+
53
+ 1. Clone the repository:
54
+ ```bash
55
+ git clone https://github.com/your-username/secondbrainos-mcp-server.git
56
+ cd secondbrainos-mcp-server
57
+ ```
58
+
59
+ 2. Install dependencies:
60
+ ```bash
61
+ npm install
62
+ ```
63
+
64
+ 3. Create a `.env` file from the example:
65
+ ```bash
66
+ cp .env.example .env
67
+ # Edit .env with your actual USER_ID and USER_SECRET
68
+ ```
69
+
70
+ ### Testing the Schema Conversion
38
71
 
39
- If you encounter issues, check the Claude Desktop logs:
72
+ To see how your OpenAPI schema is converted to MCP tools:
40
73
 
41
74
  ```bash
42
- tail -f ~/Library/Logs/Claude/mcp*.log
75
+ npm run test-conversion
76
+ ```
77
+
78
+ This will:
79
+ - Fetch your OpenAPI schema from Second Brain OS
80
+ - Convert it to LLM function calling format
81
+ - Display all available functions and their schemas
82
+ - Show any conversion errors
83
+ - Demonstrate the MCP tool format
84
+
85
+ ### Development Scripts
86
+
87
+ - `npm run build` - Compile TypeScript to JavaScript
88
+ - `npm run dev` - Run the MCP server in development mode
89
+ - `npm run test-conversion` - Test the OpenAPI to MCP conversion
90
+ - `npm start` - Run the compiled server
91
+
92
+ ### Project Structure
93
+
94
+ ```
95
+ secondbrainos-mcp-server/
96
+ ├── src/
97
+ │ ├── index.ts # Main MCP server implementation
98
+ │ └── test-conversion.ts # Schema conversion testing utility
99
+ ├── build/ # Compiled JavaScript (git-ignored)
100
+ ├── bin/
101
+ │ └── cli.js # CLI setup script
102
+ ├── .env.example # Environment variables template
103
+ ├── package.json # Package configuration
104
+ ├── tsconfig.json # TypeScript configuration
105
+ └── README.md # This file
43
106
  ```
44
107
 
45
- Common issues:
46
- - Make sure Node.js is properly installed
108
+ ## Environment Variables
109
+
110
+ The server requires the following environment variables:
111
+ - `USER_ID`: Your Second Brain OS user ID
112
+ - `USER_SECRET`: Your Second Brain OS user secret
113
+ - `API_BASE_URL` (optional): Override the default API base URL
114
+
115
+ ## How It Works
116
+
117
+ 1. **Schema Fetching**: On startup, the server fetches your personalized OpenAPI schema from Second Brain OS
118
+ 2. **Schema Conversion**: The `@samchon/openapi` library converts the schema to an optimized format for LLM function calling
119
+ 3. **MCP Tools**: Each API endpoint becomes an MCP tool that Claude can use
120
+ 4. **Function Execution**: When Claude calls a tool, the server executes the corresponding API call with proper authentication
121
+
122
+ ## Troubleshooting
123
+
124
+ ### Claude Desktop doesn't see the server
125
+ 1. Ensure Claude Desktop is completely quit before running the setup
126
+ 2. Check the configuration file was created at the correct location
127
+ 3. Review the logs at the platform-specific location mentioned during setup
128
+
129
+ ### Authentication errors
47
130
  - Verify your USER_ID and USER_SECRET are correct
48
- - Ensure you have the Claude MCP feature enabled in your account
131
+ - Ensure your account has the Claude MCP feature enabled
132
+ - Check that your credentials haven't expired
133
+
134
+ ### Schema conversion errors
135
+ - Run `npm run test-conversion` to see detailed error messages
136
+ - Some complex OpenAPI features may not be supported by LLM function calling
137
+ - Check the console output for specific endpoints that failed conversion
138
+
139
+ ## Contributing
140
+
141
+ 1. Fork the repository
142
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
143
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
144
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
145
+ 5. Open a Pull Request
49
146
 
50
- ## Support
147
+ ## License
51
148
 
52
- If you continue to experience problems, please contact support at support@secondbrainos.com
149
+ MIT
package/bin/cli.js CHANGED
File without changes
package/build/index.js CHANGED
@@ -1,20 +1,35 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
4
+ import { OpenApi, HttpLlm } from "@samchon/openapi";
4
5
  import axios from "axios";
5
6
  import dotenv from "dotenv";
6
7
  import yaml from 'js-yaml';
7
8
  dotenv.config();
8
9
  class SecondBrainOSServer {
9
10
  constructor(initialSchema) {
10
- // Initialize with provided schema instead of URL
11
- this.apiSpec = initialSchema;
11
+ this.originalSpec = initialSchema;
12
12
  this.baseUrl = process.env.API_BASE_URL || 'https://api.secondbrainos.com';
13
13
  this.userId = process.env.USER_ID || '';
14
14
  this.userSecret = process.env.USER_SECRET || '';
15
15
  if (!this.userId || !this.userSecret) {
16
16
  throw new Error("USER_ID and USER_SECRET environment variables are required");
17
17
  }
18
+ // Convert OpenAPI schema using @samchon/openapi
19
+ const document = OpenApi.convert(initialSchema);
20
+ // Create LLM application with proper model configuration
21
+ this.application = HttpLlm.application({
22
+ model: "chatgpt",
23
+ document
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
+ });
18
33
  this.server = new Server({
19
34
  name: "secondbrainos-server",
20
35
  version: "1.0.0"
@@ -27,24 +42,6 @@ class SecondBrainOSServer {
27
42
  this.setupHandlers();
28
43
  this.setupErrorHandling();
29
44
  }
30
- // Helper method to extract functions from the API spec
31
- getFunctions() {
32
- const functions = [];
33
- for (const [path, pathItem] of Object.entries(this.apiSpec.paths)) {
34
- for (const [method, operation] of Object.entries(pathItem)) {
35
- if (operation.operationId) {
36
- functions.push({
37
- operationId: operation.operationId,
38
- path,
39
- method: method.toUpperCase(),
40
- summary: operation.summary || operation.operationId,
41
- schema: operation.requestBody?.content?.['application/json']?.schema || {}
42
- });
43
- }
44
- }
45
- }
46
- return functions;
47
- }
48
45
  setupHandlers() {
49
46
  // List available resources
50
47
  this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
@@ -53,7 +50,7 @@ class SecondBrainOSServer {
53
50
  uri: 'secondbrainos://api/spec',
54
51
  name: 'Second Brain OS API Specification',
55
52
  mimeType: 'application/yaml',
56
- description: this.apiSpec?.info?.title || 'API Specification'
53
+ description: this.originalSpec?.info?.title || 'API Specification'
57
54
  }]
58
55
  };
59
56
  });
@@ -66,47 +63,67 @@ class SecondBrainOSServer {
66
63
  contents: [{
67
64
  uri: request.params.uri,
68
65
  mimeType: 'application/yaml',
69
- text: yaml.dump(this.apiSpec)
66
+ text: yaml.dump(this.originalSpec)
70
67
  }]
71
68
  };
72
69
  });
73
- // List available tools
70
+ // List available tools - now using operationId as the tool name
74
71
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
75
- const functions = this.getFunctions();
76
- return {
77
- tools: functions.map(func => ({
78
- name: func.operationId,
79
- description: func.summary,
80
- inputSchema: func.schema
81
- }))
82
- };
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,
78
+ inputSchema: func.parameters // Convert to MCP format
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 };
83
93
  });
84
- // Handle tool calls
94
+ // Handle tool calls - now supporting both operationId and original names
85
95
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
86
- const functions = this.getFunctions();
87
- const func = functions.find(f => f.operationId === request.params.name);
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
+ }
88
102
  if (!func) {
89
103
  throw new McpError(ErrorCode.MethodNotFound, `Unknown function: ${request.params.name}`);
90
104
  }
91
105
  try {
92
- const bearerToken = `${this.userId}:${this.userSecret}`;
93
- const response = await axios({
94
- method: func.method,
95
- url: `${this.baseUrl}${func.path}`,
96
- data: request.params.arguments,
97
- headers: {
98
- 'Content-Type': 'application/json',
99
- 'Authorization': `Bearer ${bearerToken}`
100
- }
106
+ // Use HttpLlm.execute for better error handling and type safety
107
+ const result = await HttpLlm.execute({
108
+ connection: {
109
+ host: this.baseUrl,
110
+ headers: {
111
+ 'Authorization': `Bearer ${this.userId}:${this.userSecret}`
112
+ }
113
+ },
114
+ application: this.application, // Type assertion to avoid generic issues
115
+ function: func,
116
+ input: request.params.arguments || {}
101
117
  });
102
118
  return {
103
119
  content: [{
104
120
  type: 'text',
105
- text: JSON.stringify(response.data, null, 2)
121
+ text: JSON.stringify(result, null, 2)
106
122
  }]
107
123
  };
108
124
  }
109
125
  catch (error) {
126
+ // Better error handling with HttpLlm
110
127
  if (axios.isAxiosError(error)) {
111
128
  const status = error.response?.status;
112
129
  const errorMessage = error.response?.data?.error || error.message;
@@ -135,12 +152,21 @@ class SecondBrainOSServer {
135
152
  isError: true
136
153
  };
137
154
  }
155
+ else if (error instanceof Error) {
156
+ return {
157
+ content: [{
158
+ type: 'text',
159
+ text: `Error calling function: ${error.message}`
160
+ }],
161
+ isError: true
162
+ };
163
+ }
138
164
  throw error;
139
165
  }
140
166
  });
141
167
  }
142
168
  setupErrorHandling() {
143
- // Add any error handling setup here
169
+ // Error handling is now built into HttpLlm.execute
144
170
  // This method is kept for future error handling implementations
145
171
  }
146
172
  async run() {
@@ -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.4",
3
+ "version": "1.0.6",
4
4
  "description": "Second Brain OS MCP Server for Claude Desktop",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -8,8 +8,17 @@
8
8
  "bin": {
9
9
  "secondbrainos-mcp": "bin/cli.js"
10
10
  },
11
+ "files": [
12
+ "build/",
13
+ "bin/",
14
+ "LICENSE",
15
+ "README.md"
16
+ ],
11
17
  "scripts": {
12
18
  "build": "tsc",
19
+ "dev": "tsx src/index.ts",
20
+ "start": "node build/index.js",
21
+ "test-conversion": "tsx src/test-mcp-conversion.ts",
13
22
  "prepublishOnly": "npm run build",
14
23
  "test": "echo \"Error: no test specified\" && exit 1"
15
24
  },
@@ -23,6 +32,7 @@
23
32
  "license": "MIT",
24
33
  "dependencies": {
25
34
  "@modelcontextprotocol/sdk": "^1.0.0",
35
+ "@samchon/openapi": "^2.0.0",
26
36
  "axios": "^1.8.2",
27
37
  "dotenv": "^16.4.7",
28
38
  "js-yaml": "^4.1.0"
@@ -31,6 +41,7 @@
31
41
  "@types/js-yaml": "^4.0.5",
32
42
  "@types/node": "^20.8.10",
33
43
  "ts-node": "^10.9.1",
44
+ "tsx": "^4.7.0",
34
45
  "typescript": "^5.2.2"
35
46
  },
36
47
  "engines": {
package/.env DELETED
@@ -1,2 +0,0 @@
1
- USER_ID=
2
- USER_SECRET=
package/gitignore.txt DELETED
@@ -1,130 +0,0 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- yarn-debug.log*
6
- yarn-error.log*
7
- lerna-debug.log*
8
- .pnpm-debug.log*
9
-
10
- # Diagnostic reports (https://nodejs.org/api/report.html)
11
- report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12
-
13
- # Runtime data
14
- pids
15
- *.pid
16
- *.seed
17
- *.pid.lock
18
-
19
- # Directory for instrumented libs generated by jscoverage/JSCover
20
- lib-cov
21
-
22
- # Coverage directory used by tools like istanbul
23
- coverage
24
- *.lcov
25
-
26
- # nyc test coverage
27
- .nyc_output
28
-
29
- # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30
- .grunt
31
-
32
- # Bower dependency directory (https://bower.io/)
33
- bower_components
34
-
35
- # node-waf configuration
36
- .lock-wscript
37
-
38
- # Compiled binary addons (https://nodejs.org/api/addons.html)
39
- build/Release
40
-
41
- # Dependency directories
42
- node_modules/
43
- jspm_packages/
44
-
45
- # Snowpack dependency directory (https://snowpack.dev/)
46
- web_modules/
47
-
48
- # TypeScript cache
49
- *.tsbuildinfo
50
-
51
- # Optional npm cache directory
52
- .npm
53
-
54
- # Optional eslint cache
55
- .eslintcache
56
-
57
- # Optional stylelint cache
58
- .stylelintcache
59
-
60
- # Microbundle cache
61
- .rpt2_cache/
62
- .rts2_cache_cjs/
63
- .rts2_cache_es/
64
- .rts2_cache_umd/
65
-
66
- # Optional REPL history
67
- .node_repl_history
68
-
69
- # Output of 'npm pack'
70
- *.tgz
71
-
72
- # Yarn Integrity file
73
- .yarn-integrity
74
-
75
- # dotenv environment variable files
76
- .env
77
- .env.development.local
78
- .env.test.local
79
- .env.production.local
80
- .env.local
81
-
82
- # parcel-bundler cache (https://parceljs.org/)
83
- .cache
84
- .parcel-cache
85
-
86
- # Next.js build output
87
- .next
88
- out
89
-
90
- # Nuxt.js build / generate output
91
- .nuxt
92
- dist
93
-
94
- # Gatsby files
95
- .cache/
96
- # Comment in the public line in if your project uses Gatsby and not Next.js
97
- # https://nextjs.org/blog/next-9-1#public-directory-support
98
- # public
99
-
100
- # vuepress build output
101
- .vuepress/dist
102
-
103
- # vuepress v2.x temp and cache directory
104
- .temp
105
- .cache
106
-
107
- # Docusaurus cache and generated files
108
- .docusaurus
109
-
110
- # Serverless directories
111
- .serverless/
112
-
113
- # FuseBox cache
114
- .fusebox/
115
-
116
- # DynamoDB Local files
117
- .dynamodb/
118
-
119
- # TernJS port file
120
- .tern-port
121
-
122
- # Stores VSCode versions used for testing VSCode extensions
123
- .vscode-test
124
-
125
- # yarn v2
126
- .yarn/cache
127
- .yarn/unplugged
128
- .yarn/build-state.yml
129
- .yarn/install-state.gz
130
- .pnp.*