mcpstore-cc 0.1.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.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # mcpstore
2
+
3
+ CLI to install MCPs from [MCP Store](https://www.mcpclaudecode.com) into Claude Code.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npx mcpstore-cc install <slug>
9
+ ```
10
+
11
+ No global install required — just use `npx`.
12
+
13
+ ## Commands
14
+
15
+ ### Install an MCP
16
+
17
+ ```bash
18
+ npx mcpstore-cc install ai-brain
19
+ npx mcpstore-cc install supabase-mcp --env SUPABASE_URL=https://xxx.supabase.co
20
+ ```
21
+
22
+ ### Uninstall an MCP
23
+
24
+ ```bash
25
+ npx mcpstore-cc uninstall ai-brain
26
+ ```
27
+
28
+ ### List installed MCPs
29
+
30
+ ```bash
31
+ npx mcpstore-cc list
32
+ ```
33
+
34
+ ### Search available MCPs
35
+
36
+ ```bash
37
+ npx mcpstore-cc search database
38
+ ```
39
+
40
+ ### Save your API key
41
+
42
+ ```bash
43
+ npx mcpstore-cc login <your-api-key>
44
+ ```
45
+
46
+ ## How it works
47
+
48
+ 1. Fetches MCP configuration from the MCP Store API
49
+ 2. Adds the server entry to `~/.claude/.mcp.json`
50
+ 3. Restart Claude Code to activate
51
+
52
+ ## License
53
+
54
+ MIT
@@ -0,0 +1,3 @@
1
+ export declare function installCommand(slug: string, options: {
2
+ env?: string[];
3
+ }): Promise<void>;
@@ -0,0 +1,74 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import { fetchMcp } from "../lib/api.js";
4
+ import { addMcpServer, listMcpServers, getMcpConfigPath } from "../lib/config.js";
5
+ export async function installCommand(slug, options) {
6
+ const spinner = ora(`Fetching "${slug}" from MCP Store...`).start();
7
+ try {
8
+ const mcp = await fetchMcp(slug);
9
+ spinner.succeed(`Found: ${chalk.bold(mcp.name)} v${mcp.version} by ${mcp.author_name}`);
10
+ // Check if already installed
11
+ const existing = await listMcpServers();
12
+ if (slug in existing) {
13
+ console.log(chalk.yellow(`\n⚠ "${slug}" is already installed in Claude Code.`));
14
+ console.log(chalk.gray(" Use `mcpstore uninstall " + slug + "` first to reinstall."));
15
+ return;
16
+ }
17
+ // Build server config from config_schema or npm_package
18
+ let command;
19
+ let args;
20
+ let env = {};
21
+ if (mcp.config_schema && mcp.config_schema.command) {
22
+ // Use the config_schema from the API
23
+ command = mcp.config_schema.command;
24
+ args = mcp.config_schema.args || [];
25
+ env = mcp.config_schema.env || {};
26
+ }
27
+ else if (mcp.npm_package) {
28
+ // Fallback: use npx with the npm package
29
+ command = "npx";
30
+ args = ["-y", mcp.npm_package];
31
+ }
32
+ else {
33
+ console.log(chalk.red("\n✗ This MCP has no installation config. Contact the author."));
34
+ return;
35
+ }
36
+ // Merge env vars from --env flags
37
+ if (options.env) {
38
+ for (const pair of options.env) {
39
+ const eqIdx = pair.indexOf("=");
40
+ if (eqIdx === -1) {
41
+ console.log(chalk.yellow(`\n⚠ Skipping invalid env: "${pair}" (use KEY=VALUE format)`));
42
+ continue;
43
+ }
44
+ const key = pair.slice(0, eqIdx);
45
+ const value = pair.slice(eqIdx + 1);
46
+ env[key] = value;
47
+ }
48
+ }
49
+ // Check if config_schema defines required env vars we're missing
50
+ if (mcp.config_schema?.env) {
51
+ const schemaEnv = mcp.config_schema.env;
52
+ const missingKeys = Object.keys(schemaEnv).filter((k) => !env[k] && schemaEnv[k] !== "");
53
+ if (missingKeys.length > 0) {
54
+ console.log(chalk.yellow("\n⚠ This MCP requires environment variables:"));
55
+ for (const key of missingKeys) {
56
+ console.log(chalk.gray(` ${key}=${schemaEnv[key] || "<your-value>"}`));
57
+ }
58
+ console.log(chalk.gray("\n Pass them with: mcpstore install " + slug + " --env " + missingKeys.map((k) => `${k}=<value>`).join(" --env ")));
59
+ }
60
+ }
61
+ // Write to ~/.claude/.mcp.json
62
+ const spinner2 = ora("Adding to Claude Code config...").start();
63
+ const entry = { command, args, ...(Object.keys(env).length > 0 ? { env } : {}) };
64
+ await addMcpServer(slug, entry);
65
+ spinner2.succeed("Added to Claude Code config");
66
+ console.log(chalk.green(`\n✓ ${chalk.bold(mcp.name)} installed successfully!`));
67
+ console.log(chalk.gray(` Config: ${getMcpConfigPath()}`));
68
+ console.log(chalk.gray(` Restart Claude Code to activate this MCP.\n`));
69
+ }
70
+ catch (err) {
71
+ spinner.fail(err.message);
72
+ process.exit(1);
73
+ }
74
+ }
@@ -0,0 +1 @@
1
+ export declare function listCommand(): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import chalk from "chalk";
2
+ import { listMcpServers, getMcpConfigPath } from "../lib/config.js";
3
+ export async function listCommand() {
4
+ try {
5
+ const servers = await listMcpServers();
6
+ const names = Object.keys(servers);
7
+ if (names.length === 0) {
8
+ console.log(chalk.gray("\nNo MCPs installed in Claude Code."));
9
+ console.log(chalk.gray(" Browse MCPs at https://www.mcpclaudecode.com/browse"));
10
+ console.log(chalk.gray(" Install one with: mcpstore install <slug>\n"));
11
+ return;
12
+ }
13
+ console.log(chalk.bold(`\n${names.length} MCP(s) installed in Claude Code:\n`));
14
+ for (const name of names) {
15
+ const server = servers[name];
16
+ const cmdStr = [server.command, ...server.args].join(" ");
17
+ console.log(` ${chalk.green("●")} ${chalk.bold(name)}`);
18
+ console.log(chalk.gray(` ${cmdStr}`));
19
+ if (server.env && Object.keys(server.env).length > 0) {
20
+ const envKeys = Object.keys(server.env);
21
+ console.log(chalk.gray(` env: ${envKeys.join(", ")}`));
22
+ }
23
+ console.log();
24
+ }
25
+ console.log(chalk.gray(`Config: ${getMcpConfigPath()}\n`));
26
+ }
27
+ catch (err) {
28
+ console.error(chalk.red(err.message));
29
+ process.exit(1);
30
+ }
31
+ }
@@ -0,0 +1,2 @@
1
+ export declare function loginCommand(apiKey: string): Promise<void>;
2
+ export declare function whoamiCommand(): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import chalk from "chalk";
2
+ import { saveApiKey, getStoredApiKey } from "../lib/config.js";
3
+ export async function loginCommand(apiKey) {
4
+ try {
5
+ await saveApiKey(apiKey);
6
+ console.log(chalk.green("\n✓ API key saved successfully!"));
7
+ console.log(chalk.gray(" Stored in ~/.mcpstore/config.json\n"));
8
+ }
9
+ catch (err) {
10
+ console.error(chalk.red(err.message));
11
+ process.exit(1);
12
+ }
13
+ }
14
+ export async function whoamiCommand() {
15
+ const key = await getStoredApiKey();
16
+ if (!key) {
17
+ console.log(chalk.gray("\nNot logged in."));
18
+ console.log(chalk.gray(" Run: mcpstore login <your-api-key>\n"));
19
+ return;
20
+ }
21
+ const masked = key.slice(0, 8) + "..." + key.slice(-4);
22
+ console.log(`\nLogged in with API key: ${chalk.bold(masked)}\n`);
23
+ }
@@ -0,0 +1 @@
1
+ export declare function searchCommand(query?: string): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import { searchMcps } from "../lib/api.js";
4
+ export async function searchCommand(query) {
5
+ const spinner = ora(query ? `Searching for "${query}"...` : "Fetching MCPs...").start();
6
+ try {
7
+ const mcps = await searchMcps(query);
8
+ spinner.stop();
9
+ if (mcps.length === 0) {
10
+ console.log(chalk.gray("\nNo MCPs found."));
11
+ if (query) {
12
+ console.log(chalk.gray(` Try a different search term or browse at https://www.mcpclaudecode.com/browse\n`));
13
+ }
14
+ return;
15
+ }
16
+ console.log(chalk.bold(`\n${mcps.length} MCP(s) found:\n`));
17
+ for (const mcp of mcps) {
18
+ const badge = mcp.plan_required !== "free" ? chalk.yellow(` [${mcp.plan_required.toUpperCase()}]`) : "";
19
+ const stars = mcp.rating_avg > 0 ? chalk.yellow(` ★ ${mcp.rating_avg.toFixed(1)}`) : "";
20
+ const installs = mcp.install_count > 0 ? chalk.gray(` (${mcp.install_count} installs)`) : "";
21
+ console.log(` ${chalk.bold(mcp.name)}${badge}${stars}${installs}`);
22
+ console.log(chalk.gray(` ${mcp.short_description || mcp.description?.slice(0, 80) || ""}`));
23
+ console.log(chalk.cyan(` mcpstore install ${mcp.slug}`));
24
+ console.log();
25
+ }
26
+ }
27
+ catch (err) {
28
+ spinner.fail(err.message);
29
+ process.exit(1);
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ export declare function uninstallCommand(slug: string): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import chalk from "chalk";
2
+ import ora from "ora";
3
+ import { removeMcpServer, getMcpConfigPath } from "../lib/config.js";
4
+ export async function uninstallCommand(slug) {
5
+ const spinner = ora(`Removing "${slug}" from Claude Code...`).start();
6
+ try {
7
+ const removed = await removeMcpServer(slug);
8
+ if (!removed) {
9
+ spinner.warn(`"${slug}" is not installed in Claude Code.`);
10
+ return;
11
+ }
12
+ spinner.succeed(`Removed "${slug}" from Claude Code config`);
13
+ console.log(chalk.green(`\n✓ ${chalk.bold(slug)} uninstalled successfully!`));
14
+ console.log(chalk.gray(` Config: ${getMcpConfigPath()}`));
15
+ console.log(chalk.gray(` Restart Claude Code to apply changes.\n`));
16
+ }
17
+ catch (err) {
18
+ spinner.fail(err.message);
19
+ process.exit(1);
20
+ }
21
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { installCommand } from "./commands/install.js";
4
+ import { uninstallCommand } from "./commands/uninstall.js";
5
+ import { listCommand } from "./commands/list.js";
6
+ import { searchCommand } from "./commands/search.js";
7
+ import { loginCommand, whoamiCommand } from "./commands/login.js";
8
+ const program = new Command();
9
+ program
10
+ .name("mcpstore")
11
+ .description("Install MCPs from MCP Store into Claude Code")
12
+ .version("0.1.0");
13
+ program
14
+ .command("install <slug>")
15
+ .description("Install an MCP into Claude Code")
16
+ .option("-e, --env <KEY=VALUE...>", "Set environment variables", (val, acc) => {
17
+ acc.push(val);
18
+ return acc;
19
+ }, [])
20
+ .action(async (slug, opts) => {
21
+ await installCommand(slug, { env: opts.env.length > 0 ? opts.env : undefined });
22
+ });
23
+ program
24
+ .command("uninstall <slug>")
25
+ .description("Remove an MCP from Claude Code")
26
+ .action(async (slug) => {
27
+ await uninstallCommand(slug);
28
+ });
29
+ program
30
+ .command("list")
31
+ .description("List all MCPs installed in Claude Code")
32
+ .action(async () => {
33
+ await listCommand();
34
+ });
35
+ program
36
+ .command("search [query]")
37
+ .description("Search MCPs on MCP Store")
38
+ .action(async (query) => {
39
+ await searchCommand(query);
40
+ });
41
+ program
42
+ .command("login <api-key>")
43
+ .description("Save your MCP Store API key")
44
+ .action(async (apiKey) => {
45
+ await loginCommand(apiKey);
46
+ });
47
+ program
48
+ .command("whoami")
49
+ .description("Show current logged-in API key")
50
+ .action(async () => {
51
+ await whoamiCommand();
52
+ });
53
+ program.parse();
@@ -0,0 +1,22 @@
1
+ export interface McpConfig {
2
+ command: string;
3
+ args: string[];
4
+ env?: Record<string, string>;
5
+ }
6
+ export interface McpDetail {
7
+ id: string;
8
+ name: string;
9
+ slug: string;
10
+ description: string;
11
+ short_description: string;
12
+ author_name: string;
13
+ npm_package: string | null;
14
+ version: string;
15
+ plan_required: string;
16
+ install_count: number;
17
+ rating_avg: number;
18
+ rating_count: number;
19
+ config_schema: McpConfig | null;
20
+ }
21
+ export declare function fetchMcp(slug: string): Promise<McpDetail>;
22
+ export declare function searchMcps(query?: string): Promise<McpDetail[]>;
@@ -0,0 +1,24 @@
1
+ const API_BASE = "https://aibrain-mcp-backend.vercel.app/v1";
2
+ export async function fetchMcp(slug) {
3
+ const res = await fetch(`${API_BASE}/marketplace/mcps/${slug}`);
4
+ if (!res.ok) {
5
+ if (res.status === 404) {
6
+ throw new Error(`MCP "${slug}" not found on MCP Store.`);
7
+ }
8
+ throw new Error(`API error: ${res.status} ${res.statusText}`);
9
+ }
10
+ const data = await res.json();
11
+ return data.mcp ?? data;
12
+ }
13
+ export async function searchMcps(query) {
14
+ const params = new URLSearchParams();
15
+ if (query)
16
+ params.set("search", query);
17
+ params.set("limit", "50");
18
+ const res = await fetch(`${API_BASE}/marketplace/mcps?${params}`);
19
+ if (!res.ok) {
20
+ throw new Error(`API error: ${res.status} ${res.statusText}`);
21
+ }
22
+ const data = await res.json();
23
+ return data.mcps ?? data;
24
+ }
@@ -0,0 +1,17 @@
1
+ export interface McpServerEntry {
2
+ command: string;
3
+ args: string[];
4
+ env?: Record<string, string>;
5
+ }
6
+ interface McpConfigFile {
7
+ mcpServers: Record<string, McpServerEntry>;
8
+ }
9
+ export declare function readMcpConfig(): Promise<McpConfigFile>;
10
+ export declare function writeMcpConfig(config: McpConfigFile): Promise<void>;
11
+ export declare function addMcpServer(name: string, entry: McpServerEntry): Promise<void>;
12
+ export declare function removeMcpServer(name: string): Promise<boolean>;
13
+ export declare function listMcpServers(): Promise<Record<string, McpServerEntry>>;
14
+ export declare function getStoredApiKey(): Promise<string | null>;
15
+ export declare function saveApiKey(apiKey: string): Promise<void>;
16
+ export declare function getMcpConfigPath(): string;
17
+ export {};
@@ -0,0 +1,57 @@
1
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
2
+ import { existsSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ // ── Claude Code MCP config ──────────────────────────────────────
6
+ const CLAUDE_DIR = join(homedir(), ".claude");
7
+ const MCP_CONFIG_PATH = join(CLAUDE_DIR, ".mcp.json");
8
+ export async function readMcpConfig() {
9
+ if (!existsSync(MCP_CONFIG_PATH)) {
10
+ return { mcpServers: {} };
11
+ }
12
+ const raw = await readFile(MCP_CONFIG_PATH, "utf-8");
13
+ return JSON.parse(raw);
14
+ }
15
+ export async function writeMcpConfig(config) {
16
+ if (!existsSync(CLAUDE_DIR)) {
17
+ await mkdir(CLAUDE_DIR, { recursive: true });
18
+ }
19
+ await writeFile(MCP_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
20
+ }
21
+ export async function addMcpServer(name, entry) {
22
+ const config = await readMcpConfig();
23
+ config.mcpServers[name] = entry;
24
+ await writeMcpConfig(config);
25
+ }
26
+ export async function removeMcpServer(name) {
27
+ const config = await readMcpConfig();
28
+ if (!(name in config.mcpServers)) {
29
+ return false;
30
+ }
31
+ delete config.mcpServers[name];
32
+ await writeMcpConfig(config);
33
+ return true;
34
+ }
35
+ export async function listMcpServers() {
36
+ const config = await readMcpConfig();
37
+ return config.mcpServers;
38
+ }
39
+ // ── mcpstore CLI config (API key storage) ───────────────────────
40
+ const MCPSTORE_DIR = join(homedir(), ".mcpstore");
41
+ const MCPSTORE_CONFIG = join(MCPSTORE_DIR, "config.json");
42
+ export async function getStoredApiKey() {
43
+ if (!existsSync(MCPSTORE_CONFIG))
44
+ return null;
45
+ const raw = await readFile(MCPSTORE_CONFIG, "utf-8");
46
+ const data = JSON.parse(raw);
47
+ return data.apiKey ?? null;
48
+ }
49
+ export async function saveApiKey(apiKey) {
50
+ if (!existsSync(MCPSTORE_DIR)) {
51
+ await mkdir(MCPSTORE_DIR, { recursive: true });
52
+ }
53
+ await writeFile(MCPSTORE_CONFIG, JSON.stringify({ apiKey }, null, 2) + "\n", "utf-8");
54
+ }
55
+ export function getMcpConfigPath() {
56
+ return MCP_CONFIG_PATH;
57
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "mcpstore-cc",
3
+ "version": "0.1.0",
4
+ "description": "CLI to install MCPs from MCP Store into Claude Code",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcpstore": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": ["mcp", "claude-code", "marketplace", "cli"],
15
+ "author": "MCP Store",
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "commander": "^12.1.0",
19
+ "chalk": "^5.3.0",
20
+ "ora": "^8.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.0.0",
24
+ "typescript": "^5.6.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ]
32
+ }