secondbrainos-mcp-server 1.0.8 → 1.0.9

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
@@ -41,6 +41,13 @@ The server uses `@samchon/openapi` library for robust OpenAPI handling, providin
41
41
  - Automatic request formatting based on OpenAPI specifications
42
42
  - Support for complex parameters and nested objects
43
43
 
44
+ ### Prompts (Workflows)
45
+ The server exposes your Second Brain OS workflows as MCP prompts, making them available in Claude Desktop's attach menu:
46
+ - Automatically discovers your workflows via the `runPromptChain` service
47
+ - Each workflow appears as a selectable prompt with its name and description
48
+ - Selecting a prompt fetches the full prompt chain (ordered instructions) and injects them into the conversation
49
+ - Supports an optional `user_input` argument to provide additional context
50
+
44
51
  ### Better Error Handling
45
52
  - Detailed error messages for debugging
46
53
  - Proper handling of authentication failures, bad requests, and service errors
@@ -117,7 +124,8 @@ The server requires the following environment variables:
117
124
  1. **Schema Fetching**: On startup, the server fetches your personalized OpenAPI schema from Second Brain OS
118
125
  2. **Schema Conversion**: The `@samchon/openapi` library converts the schema to an optimized format for LLM function calling
119
126
  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
127
+ 4. **MCP Prompts**: If the `runPromptChain` service is available, your workflows are exposed as selectable prompts in the client UI
128
+ 5. **Function Execution**: When Claude calls a tool, the server executes the corresponding API call with proper authentication
121
129
 
122
130
  ## Troubleshooting
123
131
 
package/bin/cli.js CHANGED
@@ -158,7 +158,7 @@ async function main() {
158
158
 
159
159
  // Verify user with the cloud function
160
160
  try {
161
- const verificationUrl = 'https://us-central1-second-brain-os.cloudfunctions.net/verify-user-data-public';
161
+ const verificationUrl = 'https://us-central1-second-brain-os.cloudfunctions.net/gcf-sbos-verifyuserdatapublic';
162
162
  const response = await axios.post(verificationUrl, {
163
163
  "x-user-api-key": `Bearer ${USER_ID}:${USER_SECRET}`
164
164
  });
package/build/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
3
+ import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
4
4
  import { OpenApi, HttpLlm } from "@samchon/openapi";
5
5
  import axios from "axios";
6
6
  import dotenv from "dotenv";
@@ -30,13 +30,30 @@ class SecondBrainOSServer {
30
30
  this.functionMap.set(operation.operationId, func);
31
31
  }
32
32
  });
33
+ // Initialize workflow name-to-ID map
34
+ this.workflowNameToId = new Map();
35
+ // Discover runPromptChain path from the OpenAPI schema
36
+ this.runPromptChainPath = null;
37
+ if (initialSchema.paths) {
38
+ for (const [path, pathItem] of Object.entries(initialSchema.paths)) {
39
+ for (const operation of Object.values(pathItem)) {
40
+ if (operation.operationId === 'runPromptChain') {
41
+ this.runPromptChainPath = path;
42
+ break;
43
+ }
44
+ }
45
+ if (this.runPromptChainPath)
46
+ break;
47
+ }
48
+ }
33
49
  this.server = new Server({
34
50
  name: "secondbrainos-server",
35
51
  version: "1.0.0"
36
52
  }, {
37
53
  capabilities: {
38
54
  resources: {},
39
- tools: {}
55
+ tools: {},
56
+ prompts: {}
40
57
  }
41
58
  });
42
59
  this.setupHandlers();
@@ -164,6 +181,96 @@ class SecondBrainOSServer {
164
181
  throw error;
165
182
  }
166
183
  });
184
+ // List available prompts (user's workflows)
185
+ this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
186
+ if (!this.runPromptChainPath) {
187
+ return { prompts: [] };
188
+ }
189
+ try {
190
+ const data = await this.callRunPromptChain('', '');
191
+ const workflows = data.workflows || [];
192
+ // Populate name-to-ID map
193
+ this.workflowNameToId.clear();
194
+ for (const wf of workflows) {
195
+ if (wf.name && wf.workflow_id) {
196
+ this.workflowNameToId.set(wf.name, wf.workflow_id);
197
+ }
198
+ }
199
+ return {
200
+ prompts: workflows.map((wf) => ({
201
+ name: wf.name || wf.workflow_id,
202
+ description: wf.description || wf.name,
203
+ arguments: [
204
+ {
205
+ name: "user_input",
206
+ description: "Optional context or input to guide the workflow execution",
207
+ required: false
208
+ }
209
+ ]
210
+ }))
211
+ };
212
+ }
213
+ catch (error) {
214
+ console.error('Failed to fetch workflows for prompts/list:', error);
215
+ return { prompts: [] };
216
+ }
217
+ });
218
+ // Get a specific prompt (workflow's prompt chain)
219
+ this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
220
+ const promptName = request.params.name;
221
+ const workflowId = this.workflowNameToId.get(promptName) || promptName;
222
+ const userInput = request.params.arguments?.user_input;
223
+ // Fetch workflow details to get prompt IDs
224
+ const workflowData = await this.callRunPromptChain('workflow', workflowId);
225
+ const promptIds = workflowData.prompt_id || [];
226
+ if (promptIds.length === 0) {
227
+ throw new McpError(ErrorCode.InvalidRequest, `No prompts found for workflow: ${workflowId}`);
228
+ }
229
+ // Fetch each prompt's instructions
230
+ const promptResults = await Promise.all(promptIds.map((promptId) => this.callRunPromptChain('prompt', promptId)));
231
+ // Sort by order and build messages
232
+ const sortedPrompts = promptResults.sort((a, b) => (a.order || 0) - (b.order || 0));
233
+ const messages = [];
234
+ for (const prompt of sortedPrompts) {
235
+ messages.push({
236
+ role: "user",
237
+ content: {
238
+ type: "text",
239
+ text: prompt.instructions
240
+ }
241
+ });
242
+ }
243
+ // If user provided input, append it as the final message
244
+ if (userInput) {
245
+ messages.push({
246
+ role: "user",
247
+ content: {
248
+ type: "text",
249
+ text: userInput
250
+ }
251
+ });
252
+ }
253
+ return {
254
+ description: `Workflow prompt chain (${sortedPrompts.length} steps)`,
255
+ messages
256
+ };
257
+ });
258
+ }
259
+ async callRunPromptChain(entity, entityId) {
260
+ if (!this.runPromptChainPath) {
261
+ throw new McpError(ErrorCode.InternalError, 'runPromptChain service not available for this user');
262
+ }
263
+ const url = `${this.baseUrl}${this.runPromptChainPath}`;
264
+ const response = await axios.post(url, {
265
+ entity,
266
+ entity_id: entityId
267
+ }, {
268
+ headers: {
269
+ 'Authorization': `Bearer ${this.userId}:${this.userSecret}`,
270
+ 'Content-Type': 'application/json'
271
+ }
272
+ });
273
+ return response.data;
167
274
  }
168
275
  setupErrorHandling() {
169
276
  // Error handling is now built into HttpLlm.execute
@@ -179,7 +286,7 @@ class SecondBrainOSServer {
179
286
  async function fetchSchema() {
180
287
  const userId = process.env.USER_ID;
181
288
  try {
182
- const response = await axios.post('https://us-central1-second-brain-os.cloudfunctions.net/generate-open-api-schema', { user_id: userId }, {
289
+ const response = await axios.post('https://schema.secondbrainos.com', { user_id: userId }, {
183
290
  headers: {
184
291
  'Content-Type': 'application/json'
185
292
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secondbrainos-mcp-server",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Second Brain OS MCP Server for Claude Desktop",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -9,7 +9,7 @@
9
9
  "secondbrainos-mcp": "bin/cli.js"
10
10
  },
11
11
  "files": [
12
- "build/",
12
+ "build/index.js",
13
13
  "bin/",
14
14
  "LICENSE",
15
15
  "README.md"
@@ -1,135 +0,0 @@
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);