Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
auth-prompter.ts•4.79 kB
/** * Interactive authentication field prompter * * Prompts users for required authentication fields based on detected requirements */ import * as readline from 'readline'; import type { AuthField } from './auth-detector.js'; export interface AuthValues { token?: string; username?: string; password?: string; clientId?: string; clientSecret?: string; deviceAuthUrl?: string; tokenUrl?: string; scopes?: string[]; } export class AuthPrompter { private rl: readline.Interface; constructor() { this.rl = readline.createInterface({ input: process.stdin, output: process.stdout }); } /** * Prompt user for all required fields */ async promptFields(fields: AuthField[]): Promise<AuthValues> { const values: AuthValues = {}; console.log(''); // blank line for spacing for (const field of fields) { const value = await this.promptField(field); if (value) { // Handle special fields if (field.name === 'scopes') { // Split comma-separated scopes values.scopes = value.split(',').map(s => s.trim()).filter(s => s.length > 0); } else { (values as any)[field.name] = value; } } else if (field.required) { throw new Error(`Required field '${field.label}' cannot be empty`); } } this.close(); return values; } /** * Prompt for a single field */ private async promptField(field: AuthField): Promise<string> { const label = field.required ? `${field.label} *` : field.label; const placeholder = field.placeholder ? ` (${field.placeholder})` : ''; const prompt = `${label}${placeholder}: `; // Show description if available if (field.description) { console.log(`\x1b[2m${field.description}\x1b[0m`); } const value = await this.prompt(prompt, field.type === 'password'); // Use placeholder as default if value is empty and field is not required if (!value && !field.required && field.placeholder) { return field.placeholder; } return value; } /** * Prompt for input (with optional masking for passwords) */ private async prompt(question: string, maskInput: boolean = false): Promise<string> { return new Promise((resolve) => { if (maskInput) { // For password fields, disable echo const stdin = process.stdin; const originalRawMode = stdin.isRaw; stdin.setRawMode(true); process.stdout.write(question); let input = ''; stdin.on('data', function onData(char: Buffer) { const c = char.toString('utf8'); switch (c) { case '\n': case '\r': case '\u0004': // Ctrl+D stdin.setRawMode(originalRawMode); stdin.removeListener('data', onData); process.stdout.write('\n'); resolve(input); break; case '\u0003': // Ctrl+C process.exit(0); break; case '\u007f': // Backspace if (input.length > 0) { input = input.slice(0, -1); process.stdout.write('\b \b'); // Erase character } break; default: input += c; process.stdout.write('*'); // Show asterisk for each character break; } }); } else { // Normal text input this.rl.question(question, (answer) => { resolve(answer.trim()); }); } }); } /** * Close the readline interface */ close(): void { this.rl.close(); } /** * Display detected auth information */ static displayDetected(authType: string, detected: any): void { console.log('\x1b[36m🔍 Authentication detected:\x1b[0m'); console.log(` Type: \x1b[1m${authType}\x1b[0m`); if (detected.statusCode) { console.log(` HTTP Status: ${detected.statusCode}`); } if (detected.wwwAuthenticate) { console.log(` WWW-Authenticate: ${detected.wwwAuthenticate}`); } if (detected.oauthEndpoints) { console.log(` OAuth endpoints discovered automatically`); } if (detected.errorMessage) { console.log(` Message: ${detected.errorMessage}`); } console.log(''); } /** * Confirm with user before proceeding */ static async confirm(message: string): Promise<boolean> { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(`${message} (y/n): `, (answer) => { rl.close(); resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'); }); }); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/portel-dev/ncp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server