Skip to content

Commit

Permalink
refactor: update vendor and license information in package list and i…
Browse files Browse the repository at this point in the history
…mprove installation process

- Updated vendor information for multiple MCP servers to include a link to Anthropic, PBC.
- Changed license status from 'Unknown' to 'MIT' for several packages in the package list.
- Refactored the installation process to prompt for runtime when installing unverified packages.
- Created a utility function to handle unknown packages with basic metadata.
- Improved error handling and user prompts during package installation.

These changes enhance the clarity of package information and streamline the installation workflow.
  • Loading branch information
michaellatman committed Dec 3, 2024
1 parent 9712391 commit 5e9f4ad
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 145 deletions.
14 changes: 7 additions & 7 deletions packages/package-list.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
{
"name": "mcp-server-fetch",
"description": "A Model Context Protocol server providing tools to fetch and convert web content for usage by LLMs",
"vendor": "Anthropic, PBC.",
"vendor": "Anthropic, PBC (https://anthropic.com)",
"sourceUrl": "https://github.com/modelcontextprotocol/servers/blob/main/src/fetch",
"homepage": "https://github.com/modelcontextprotocol/servers",
"license": "MIT",
Expand All @@ -200,7 +200,7 @@
{
"name": "mcp-server-git",
"description": "A Model Context Protocol server providing tools to read, search, and manipulate Git repositories programmatically via LLMs",
"vendor": "Anthropic, PBC.",
"vendor": "Anthropic, PBC (https://anthropic.com)",
"sourceUrl": "https://github.com/modelcontextprotocol/servers/blob/main/src/git",
"homepage": "https://github.com/modelcontextprotocol/servers",
"license": "MIT",
Expand All @@ -209,25 +209,25 @@
{
"name": "mcp-server-sentry",
"description": "MCP server for retrieving issues from sentry.io",
"vendor": "Unknown",
"vendor": "Anthropic, PBC (https://anthropic.com)",
"sourceUrl": "https://github.com/modelcontextprotocol/servers/blob/main/src/sentry",
"homepage": "https://github.com/modelcontextprotocol/servers",
"license": "Unknown",
"license": "MIT",
"runtime": "python"
},
{
"name": "mcp-server-sqlite",
"description": "A simple SQLite MCP server",
"vendor": "Unknown",
"vendor": "Anthropic, PBC (https://anthropic.com)",
"sourceUrl": "https://github.com/modelcontextprotocol/servers/blob/main/src/sqlite",
"homepage": "https://github.com/modelcontextprotocol/servers",
"license": "Unknown",
"license": "MIT",
"runtime": "python"
},
{
"name": "mcp-server-time",
"description": "A Model Context Protocol server providing tools for time queries and timezone conversions for LLMs",
"vendor": "Mariusz 'maledorak' Korzekwa",
"vendor": "Anthropic, PBC (https://anthropic.com)",
"sourceUrl": "https://github.com/modelcontextprotocol/servers/blob/main/src/time",
"homepage": "https://github.com/modelcontextprotocol/servers",
"license": "MIT",
Expand Down
12 changes: 4 additions & 8 deletions src/extractors/modelcontextprotocol-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,11 @@ async function extractPythonPackage(pyprojectPath: string, repoUrl: string, subP

const { project } = pyproject;

// Get the first author/maintainer for vendor info
const vendor = project.authors?.[0]?.name ||
project.maintainers?.[0]?.name ||
'Unknown';
// Set vendor to Anthropic
const vendor = 'Anthropic, PBC (https://anthropic.com)';

// Handle license field which can be either a string or an object
const license = typeof project.license === 'string'
? project.license
: project.license?.text || 'Unknown';
// Set license to MIT
const license = 'MIT';

return {
name: project.name || '',
Expand Down
165 changes: 36 additions & 129 deletions src/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,151 +3,52 @@ import { join } from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { Package } from './types/index.js';
import { installMCPServer } from './utils/config.js';
import { installPackage as installPkg } from './utils/package-management.js';
import inquirer from 'inquirer';
import { exec } from 'child_process';
import { promisify } from 'util';
import { packageHelpers } from './helpers/index.js';
import chalk from 'chalk';

const execAsync = promisify(exec);

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const packageListPath = join(__dirname, '../packages/package-list.json');

async function handlePackageHelper(packageName: string): Promise<Record<string, string> | undefined> {
const helper = packageHelpers[packageName];
if (!helper?.requiredEnvVars) return undefined;

const envVars: Record<string, string> = {};
console.log(chalk.cyan('\nEnvironment Variable Configuration:'));

// First check if env vars are needed and get user confirmation
console.log(chalk.yellow('\nThis package requires configuration of environment variables.'));
const { proceed } = await inquirer.prompt<{ proceed: boolean }>([{
type: 'confirm',
name: 'proceed',
message: 'Would you like to configure them now?',
default: true
}]);

if (!proceed) {
console.log(chalk.yellow('\nInstallation cancelled. Package requires environment configuration.'));
process.exit(0);
}

for (const [envVar, config] of Object.entries(helper.requiredEnvVars)) {
const envConfig = config as { description: string; required: boolean; default?: string };
const existingValue = process.env[envVar];

console.log(chalk.gray(`\n${envVar}:`));
console.log(chalk.gray(`Description: ${envConfig.description}`));
if (envConfig.default) {
console.log(chalk.gray(`Default: ${envConfig.default}`));
async function promptForRuntime(): Promise<'node' | 'python'> {
const { runtime } = await inquirer.prompt<{ runtime: 'node' | 'python' }>([
{
type: 'list',
name: 'runtime',
message: 'What runtime does this package use?',
choices: [
{ name: 'Node.js', value: 'node' },
{ name: 'Python', value: 'python' }
]
}

if (existingValue) {
const { useExisting } = await inquirer.prompt<{ useExisting: boolean }>([{
type: 'confirm',
name: 'useExisting',
message: `Found existing ${envVar} in environment. Use this value?`,
default: true
}]);

if (useExisting) {
envVars[envVar] = existingValue;
console.log(chalk.green(`Using existing ${envVar}`));
continue;
}
}

const { configure } = await inquirer.prompt([{
type: 'confirm',
name: 'configure',
message: `Would you like to configure ${envVar}${envConfig.required ? ' (required)' : ' (optional)'}?`,
default: envConfig.required
}]);

if (configure) {
const { value } = await inquirer.prompt([{
type: 'input',
name: 'value',
message: `Enter value for ${envVar}:`,
default: envConfig.default,
validate: (input) => {
if (envConfig.required && !input) {
return `${envVar} is required`;
}
return true;
}
}]);

if (value) {
envVars[envVar] = value;
console.log(chalk.green(`✓ ${envVar} configured`));
}
} else if (envConfig.required) {
console.log(chalk.yellow(`\n⚠️ Warning: ${envVar} is required but not configured. You'll need to set it manually.`));
process.exit(0);
}
}

return Object.keys(envVars).length > 0 ? envVars : undefined;
]);
return runtime;
}

export async function installPackage(pkg: Package | string): Promise<void> {
try {
const packageName = typeof pkg === 'string' ? pkg : pkg.name;

// Handle package-specific configuration and get environment variables
const env = await handlePackageHelper(packageName);

// After successful installation, update the config with env variables
installMCPServer(packageName, env);
console.log('Updated Claude desktop configuration');

// Prompt user about restarting Claude
const { shouldRestart } = await inquirer.prompt<{ shouldRestart: boolean }>([
{
type: 'confirm',
name: 'shouldRestart',
message: 'Would you like to restart the Claude desktop app to apply changes?',
default: true
}
]);
function createUnknownPackage(packageName: string, runtime: 'node' | 'python'): Package {
return {
name: packageName,
description: 'Unverified package',
runtime,
vendor: '',
sourceUrl: '',
homepage: '',
license: ''
};
}

if (shouldRestart) {
console.log('Restarting Claude desktop app...');
try {
// Kill existing Claude process
await execAsync('pkill -x Claude || true');
// Wait 2 seconds before restarting
console.log('Waiting for Claude to close...');
await new Promise(resolve => setTimeout(resolve, 2000));
// Start Claude again
await execAsync('open -a Claude');
console.log('Claude desktop app has been restarted');
} catch (error) {
console.warn('Failed to restart Claude desktop app automatically. Please restart it manually.');
}
} else {
console.log('Please restart the Claude desktop app manually to apply changes.');
}

} catch (error) {
console.error('Failed to install package:', error);
throw error;
}
export async function installPackage(pkg: Package): Promise<void> {
return installPkg(pkg);
}

export async function install(packageName: string): Promise<void> {
const packageList: Package[] = JSON.parse(readFileSync(packageListPath, 'utf-8'));

const pkg = packageList.find(p => p.name === packageName);
if (!pkg) {
console.warn(`Package ${packageName} not found in the curated list.`);
console.warn(chalk.yellow(`Package ${packageName} not found in the curated list.`));

const { proceedWithInstall } = await inquirer.prompt<{ proceedWithInstall: boolean }>([
{
Expand All @@ -159,14 +60,20 @@ export async function install(packageName: string): Promise<void> {
]);

if (proceedWithInstall) {
console.log(`Proceeding with installation of ${packageName}...`);
await installPackage(packageName);
console.log(chalk.cyan(`Proceeding with installation of ${packageName}...`));

// Prompt for runtime for unverified packages
const runtime = await promptForRuntime();

// Create a basic package object for unverified packages
const unknownPkg = createUnknownPackage(packageName, runtime);
await installPkg(unknownPkg);
} else {
console.log('Installation cancelled.');
process.exit(1);
}
return;
}

await installPackage(pkg);
await installPkg(pkg);
}
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export interface Package {
name: string;
description: string;
runtime: 'node' | 'python';
vendor: string;
sourceUrl: string;
homepage: string;
license: string;
runtime: 'node' | 'python';
isInstalled?: boolean;
}

Expand Down

0 comments on commit 5e9f4ad

Please sign in to comment.