secondbrainos-mcp-server 1.3.0 → 1.4.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 +106 -0
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -7,6 +7,7 @@ import dotenv from "dotenv";
7
7
  import yaml from 'js-yaml';
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
+ import { homedir } from 'os';
10
11
  dotenv.config();
11
12
  function toSnakeCase(str) {
12
13
  return str
@@ -562,6 +563,109 @@ class SecondBrainOSServer {
562
563
  this.cachedAgents = agents;
563
564
  return agents;
564
565
  }
566
+ /**
567
+ * Write Claude Code skill files to ~/.claude/skills/secondbrainos/
568
+ * Called on every server startup so skills stay in sync with SBOS.
569
+ */
570
+ async writeSkillFiles() {
571
+ const skillsBase = path.join(homedir(), '.claude', 'skills', 'secondbrainos');
572
+ // Helper to write a SKILL.md file
573
+ const writeSkill = (dir, content) => {
574
+ fs.mkdirSync(dir, { recursive: true });
575
+ fs.writeFileSync(path.join(dir, 'SKILL.md'), content, 'utf-8');
576
+ };
577
+ // 1. server-use — placeholder
578
+ writeSkill(path.join(skillsBase, 'server-use'), `---\nname: secondbrainos_server_use\ndescription: Second Brain OS server context\nuser-invocable: false\n---\n\nHello world.\n`);
579
+ // 2. tabs (agents)
580
+ if (this.getAIAgentsSchemaPath) {
581
+ try {
582
+ const agents = await this.fetchAndEnrichAgents();
583
+ for (const agent of agents) {
584
+ if (!agent.name || !agent.id)
585
+ continue;
586
+ const folderName = toSnakeCase(agent.name);
587
+ const agentDocument = {
588
+ agent_id: agent.id,
589
+ name: agent.name,
590
+ description: agent.description || '',
591
+ behaviour_and_instructions: agent.behaviour_and_instructions || '',
592
+ searchMyKnowledge_collection_id: agent.searchMyKnowledge_collection_id || '',
593
+ actions: agent.actions || [],
594
+ workflows: (agent.workflows || []).map((wf) => ({
595
+ id: wf.id,
596
+ name: wf.name,
597
+ prompts: (wf.prompts || []).map((p) => ({
598
+ id: p.id,
599
+ name: p.name,
600
+ order: p.order,
601
+ description: p.description
602
+ }))
603
+ }))
604
+ };
605
+ writeSkill(path.join(skillsBase, 'tabs', folderName), `---\nname: secondbrainos_agent_${folderName}\ndescription: "${agent.description || agent.name}"\nuser-invocable: false\n---\n\n${JSON.stringify(agentDocument, null, 2)}\n`);
606
+ }
607
+ }
608
+ catch (error) {
609
+ console.error('Failed to write agent skill files:', error);
610
+ }
611
+ }
612
+ // 3. workflows
613
+ if (this.runPromptChainPath) {
614
+ try {
615
+ const data = await this.callRunPromptChain('', '');
616
+ const workflows = data.workflows || [];
617
+ for (const wf of workflows) {
618
+ if (!wf.name || !wf.workflow_id)
619
+ continue;
620
+ const folderName = toSnakeCase(wf.name);
621
+ const workflowDetail = await this.callRunPromptChain('workflow', wf.workflow_id);
622
+ const promptIds = workflowDetail.prompt_id || [];
623
+ const promptResults = await Promise.all(promptIds.map(async (promptId) => {
624
+ try {
625
+ const promptData = await this.callRunPromptChain('prompt', promptId);
626
+ return {
627
+ id: promptId,
628
+ name: promptData.name,
629
+ order: promptData.order,
630
+ description: promptData.description || ''
631
+ };
632
+ }
633
+ catch {
634
+ return { id: promptId, name: '', order: 0, description: '' };
635
+ }
636
+ }));
637
+ const sortedPrompts = promptResults.sort((a, b) => (a.order || 0) - (b.order || 0));
638
+ const workflowDocument = {
639
+ skill_id: wf.workflow_id,
640
+ name: wf.name,
641
+ description: wf.description || '',
642
+ prompts: sortedPrompts
643
+ };
644
+ writeSkill(path.join(skillsBase, 'workflows', folderName), `---\nname: secondbrainos_skill_${folderName}\ndescription: "${wf.description || wf.name}"\nuser-invocable: false\n---\n\n${JSON.stringify(workflowDocument, null, 2)}\n`);
645
+ }
646
+ }
647
+ catch (error) {
648
+ console.error('Failed to write workflow skill files:', error);
649
+ }
650
+ }
651
+ // 4. knowledgebases
652
+ if (this.getAIAgentsSchemaPath) {
653
+ try {
654
+ const agents = await this.fetchAndEnrichAgents();
655
+ const collectionIds = agents
656
+ .map((a) => a.searchMyKnowledge_collection_id)
657
+ .filter((id) => id && id.length > 0);
658
+ if (collectionIds.length > 0) {
659
+ const kbDocument = { searchMyKnowledge_collection_ids: collectionIds };
660
+ writeSkill(path.join(skillsBase, 'knowledgebases'), `---\nname: secondbrainos_knowledge_bases\ndescription: Knowledge base collection IDs available to agents\nuser-invocable: false\n---\n\n${JSON.stringify(kbDocument, null, 2)}\n`);
661
+ }
662
+ }
663
+ catch (error) {
664
+ console.error('Failed to write knowledge bases skill file:', error);
665
+ }
666
+ }
667
+ console.error('Skill files written to ~/.claude/skills/secondbrainos/');
668
+ }
565
669
  setupErrorHandling() {
566
670
  // Error handling is now built into HttpLlm.execute
567
671
  // This method is kept for future error handling implementations
@@ -570,6 +674,8 @@ class SecondBrainOSServer {
570
674
  const transport = new StdioServerTransport();
571
675
  await this.server.connect(transport);
572
676
  console.error("Second Brain OS MCP server running on stdio");
677
+ // Write skill files in the background (non-blocking)
678
+ this.writeSkillFiles().catch(err => console.error('Failed to write skill files:', err));
573
679
  }
574
680
  }
575
681
  SecondBrainOSServer.ALLOWED_EXTENSIONS = ['.txt', '.md'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secondbrainos-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Second Brain OS MCP Server for Claude Desktop",
5
5
  "type": "module",
6
6
  "main": "build/index.js",