Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
simple-mcp-quickstart.md9.92 kB
# SimpleMCP Quick Start ## Two Ways to Create MCPs ### Option 1: Plain Class (Recommended - Like Restler!) No framework coupling - just write plain TypeScript: ```typescript /** * Weather MCP - No base class needed! */ export class Weather { async current(params: { city: string }) { return { city: params.city, temp: 72, condition: 'sunny' }; } } ``` **That's it!** Drop it in `~/.ncp/internal/weather.mcp.ts` and it works. ### Option 2: Extend SimpleMCP (Optional) For lifecycle hooks or shared utilities: ```typescript import { SimpleMCP } from 'ncp/internal-mcps/base-mcp'; export class Weather extends SimpleMCP { async onInitialize() { // Called when MCP loads } async current(params: { city: string }) { return { city: params.city, temp: 72 }; } } ``` **Both work identically!** Choose based on your needs. ## Quick Win MCPs You Can Build ### 🎯 Ultra Simple (5-10 minutes) #### 1. Math MCP ```typescript export class Math { async random(params: { min: number; max: number }) { return Math.floor(Math.random() * (params.max - params.min + 1)) + params.min; } async percentage(params: { value: number; total: number }) { return (params.value / params.total) * 100; } } ``` **Usage**: `ncp run math random --min 1 --max 100` #### 2. String MCP ```typescript export class String { async uppercase(params: { text: string }) { return params.text.toUpperCase(); } async slugify(params: { text: string }) { return params.text.toLowerCase().replace(/\s+/g, '-'); } } ``` **Usage**: `ncp run string slugify --text "Hello World"` #### 3. Encode MCP ```typescript export class Encode { async base64(params: { text: string; action: 'encode' | 'decode' }) { if (params.action === 'encode') { return Buffer.from(params.text).toString('base64'); } else { return Buffer.from(params.text, 'base64').toString('utf-8'); } } async url(params: { text: string; action: 'encode' | 'decode' }) { return params.action === 'encode' ? encodeURIComponent(params.text) : decodeURIComponent(params.text); } } ``` **Usage**: `ncp run encode base64 --text "Hello" --action encode` ### 🚀 With Dependencies (15-20 minutes) #### 4. HTTP MCP ```typescript /** * @dependencies axios@^1.6.0 */ export class HTTP { async get(params: { url: string }) { const axios = (await import('axios')).default; const response = await axios.get(params.url); return response.data; } async post(params: { url: string; data: any }) { const axios = (await import('axios')).default; const response = await axios.post(params.url, params.data); return response.data; } } ``` **Usage**: `ncp run http get --url "https://api.github.com/users/octocat"` #### 5. Date MCP ```typescript /** * @dependencies date-fns@^2.30.0 */ export class Date { async format(params: { date: string; format: string }) { const { format, parseISO } = await import('date-fns'); return format(parseISO(params.date), params.format); } async addDays(params: { date: string; days: number }) { const { addDays, parseISO, formatISO } = await import('date-fns'); return formatISO(addDays(parseISO(params.date), params.days)); } } ``` **Usage**: `ncp run date format --date "2024-01-01" --format "MMMM dd, yyyy"` #### 6. Validation MCP ```typescript /** * @dependencies validator@^13.11.0 */ export class Validation { async email(params: { email: string }) { const validator = (await import('validator')).default; return { valid: validator.isEmail(params.email) }; } async url(params: { url: string }) { const validator = (await import('validator')).default; return { valid: validator.isURL(params.url) }; } } ``` **Usage**: `ncp run validation email --email "test@example.com"` ### 💪 More Complex (30+ minutes) #### 7. GitHub MCP ```typescript /** * @dependencies octokit@^3.1.0 */ export class GitHub { async createIssue(params: { repo: string; title: string; body: string }) { const { Octokit } = await import('octokit'); const [owner, repoName] = params.repo.split('/'); const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }); const issue = await octokit.rest.issues.create({ owner, repo: repoName, title: params.title, body: params.body, }); return { number: issue.data.number, url: issue.data.html_url, }; } async listIssues(params: { repo: string }) { const { Octokit } = await import('octokit'); const [owner, repoName] = params.repo.split('/'); const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }); const issues = await octokit.rest.issues.listForRepo({ owner, repo: repoName, }); return issues.data.map(i => ({ number: i.number, title: i.title, url: i.html_url, })); } } ``` **Usage**: `ncp run github create-issue --repo "owner/repo" --title "Bug" --body "Description"` #### 8. JSON MCP ```typescript /** * @dependencies jsonpath-plus@^7.2.0 */ export class JSON { async query(params: { data: any; path: string }) { const { JSONPath } = await import('jsonpath-plus'); return JSONPath({ path: params.path, json: params.data }); } async validate(params: { data: string }) { try { JSON.parse(params.data); return { valid: true }; } catch (error: any) { return { valid: false, error: error.message }; } } } ``` **Usage**: `ncp run json query --data '{"users":[{"name":"Alice"}]}' --path "$.users[*].name"` ## Complete Example: Weather MCP ```typescript /** * Weather MCP - Get weather information * * @dependencies axios@^1.6.0 */ export class Weather { private apiKey = process.env.OPENWEATHER_API_KEY; /** * Get current weather for a city * @param city City name */ async current(params: { city: string }) { if (!this.apiKey) { throw new Error('OPENWEATHER_API_KEY environment variable not set'); } const axios = (await import('axios')).default; const response = await axios.get( `https://api.openweathermap.org/data/2.5/weather`, { params: { q: params.city, appid: this.apiKey, units: 'metric', }, } ); return { city: response.data.name, temperature: response.data.main.temp, feelsLike: response.data.main.feels_like, description: response.data.weather[0].description, humidity: response.data.main.humidity, }; } /** * Get 5-day forecast * @param city City name */ async forecast(params: { city: string }) { if (!this.apiKey) { throw new Error('OPENWEATHER_API_KEY environment variable not set'); } const axios = (await import('axios')).default; const response = await axios.get( `https://api.openweathermap.org/data/2.5/forecast`, { params: { q: params.city, appid: this.apiKey, units: 'metric', }, } ); return { city: response.data.city.name, forecasts: response.data.list.slice(0, 5).map((item: any) => ({ date: item.dt_txt, temp: item.main.temp, description: item.weather[0].description, })), }; } } ``` Save as `~/.ncp/internal/weather.mcp.ts`, then: ```bash # First time - dependencies auto-install ncp run weather current --city "London" # Returns: { "city": "London", "temperature": 15.5, "feelsLike": 14.2, "description": "light rain", "humidity": 82 } ``` ## Development Workflow ### 1. Create MCP File ```bash # Create in global directory (available everywhere) nano ~/.ncp/internal/my-tool.mcp.ts # Or project-local (only this project) nano .ncp/internal/my-tool.mcp.ts ``` ### 2. Write Class ```typescript export class MyTool { async doSomething(params: { input: string }) { return { result: params.input.toUpperCase() }; } } ``` ### 3. Test It ```bash # NCP auto-discovers and loads it ncp find "do something" # Run it ncp run my-tool do-something --input "hello" ``` ### 4. Iterate - Edit the file - Run `ncp run` again (auto-reloads) - No build step needed! ## Tips ### 1. Use Descriptive Names ```typescript // ✅ Good async convertCurrency(params: { amount: number; from: string; to: string }) // ❌ Bad async convert(params: { a: number; f: string; t: string }) ``` ### 2. Add JSDoc Comments ```typescript /** * Convert currency using live exchange rates * @param amount Amount to convert * @param from Source currency code (USD, EUR, etc.) * @param to Target currency code */ async convertCurrency(params: { amount: number; from: string; to: string }) ``` ### 3. Handle Errors Gracefully ```typescript async getData(params: { url: string }) { try { const axios = (await import('axios')).default; const response = await axios.get(params.url); return response.data; } catch (error: any) { throw new Error(`Failed to fetch data: ${error.message}`); } } ``` ### 4. Return Structured Data ```typescript // ✅ Good - structured return { success: true, data: [...], count: 5 }; // ❌ Bad - just data return [...]; ``` ## Quick Wins Summary | MCP | Lines of Code | Dependencies | Use Case | |-----|---------------|--------------|----------| | Math | ~50 | None | Random numbers, percentages, averages | | String | ~80 | None | Text manipulation | | Encode | ~30 | None | Base64, URL encoding | | HTTP | ~40 | axios | API requests | | Date | ~50 | date-fns | Date formatting | | Validation | ~40 | validator | Email, URL validation | | GitHub | ~60 | octokit | GitHub automation | | Weather | ~70 | axios | Weather data | **All under 100 lines of code!** 🎯 ## Next Steps 1. Pick a simple MCP from the list 2. Create `~/.ncp/internal/name.mcp.ts` 3. Write the class 4. Test with `ncp run` 5. Share with the community! Happy building! 🚀

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