From 7f77b72b6c8adbd9cd3f92c6b1326bf0537f519a Mon Sep 17 00:00:00 2001 From: Aaron Bockelie Date: Fri, 15 May 2026 11:31:57 -0500 Subject: [PATCH] feat: Add scripts/make-mcpb.mjs interactive maker for advanced users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Power users with multiple Obsidian vaults (or anyone wanting a custom-named bundle) run `node scripts/make-mcpb.mjs`, answer three prompts (display name, URL, API key), and get an obsidian-mcp-.mcpb with the chosen name and defaults pre-filled. Drop it into Claude Desktop — one-click install with no manual entry in the user_config dialog. Shares the zip builder with scripts/build-mcpb.mjs so there's a single source of truth for the bundle format. Zero deps; Node ≥18. This is the path ADR-102 §6 designates for multi-vault setups, replacing both the rejected N-slot manifest design and the multi-variant publishing alternative. Refs ADR-102. --- scripts/make-mcpb.mjs | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 scripts/make-mcpb.mjs diff --git a/scripts/make-mcpb.mjs b/scripts/make-mcpb.mjs new file mode 100644 index 0000000..59fdf04 --- /dev/null +++ b/scripts/make-mcpb.mjs @@ -0,0 +1,49 @@ +#!/usr/bin/env node +// Interactive .mcpb maker for advanced users. +// Prompts for name, url, api_key, then emits obsidian-mcp-.mcpb +// using the same pure-Node zip helper as the canonical build. + +import { readFileSync, writeFileSync } from 'node:fs'; +import { createInterface } from 'node:readline/promises'; +import { stdin, stdout } from 'node:process'; +import { buildMcpb } from './build-mcpb.mjs'; + +const rl = createInterface({ input: stdin, output: stdout }); + +const slugify = (s) => s.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, ''); + +const ask = async (q, fallback) => { + const a = (await rl.question(`${q}${fallback ? ` [${fallback}]` : ''}: `)).trim(); + return a || fallback || ''; +}; + +try { + console.log('Obsidian MCPB maker — generates a custom-named bundle for one vault.\n'); + + const displayName = await ask('Display name shown in Claude Desktop', 'Obsidian (Work Vault)'); + const slug = slugify(displayName) || 'obsidian-mcp-custom'; + const url = await ask('Obsidian MCP URL', 'http://localhost:3001/mcp'); + const apiKey = await ask('API key (leave blank only if plugin auth is disabled)', ''); + + const baseManifest = JSON.parse(readFileSync('mcpb/manifest.json', 'utf-8')); + const serverJs = readFileSync('mcpb/server.js', 'utf-8'); + + const manifest = { + ...baseManifest, + name: slug, + display_name: displayName, + user_config: { + ...baseManifest.user_config, + url: { ...baseManifest.user_config.url, default: url }, + api_key: { ...baseManifest.user_config.api_key, default: apiKey }, + }, + }; + + const out = `obsidian-mcp-${slug}.mcpb`; + writeFileSync(out, buildMcpb({ manifest, serverJs })); + console.log(`\n✅ Built ${out}`); + console.log(` Drop it into Claude Desktop to install "${displayName}".`); + console.log(' The URL and API key are pre-filled — just click Install.'); +} finally { + rl.close(); +}