#!/usr/bin/env node /** * Oranguru MCP Server * Provides documentation and code generation for Oranguru components */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { readFileSync } from 'fs'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Component documentation data const COMPONENTS = { Boxer: { component: 'Boxer', description: 'Advanced combobox/select with virtualization and server-side data support', examples: { basic: `import { Boxer } from '@warkypublic/oranguru'; import { useState } from 'react'; const sampleData = [ { label: 'Apple', value: 'apple' }, { label: 'Banana', value: 'banana' }, { label: 'Cherry', value: 'cherry' } ]; function MyComponent() { const [value, setValue] = useState(null); return ( ); }`, multiSelect: `import { Boxer } from '@warkypublic/oranguru'; import { useState } from 'react'; function MultiSelectExample() { const [values, setValues] = useState([]); return ( ); }`, server: `import { Boxer } from '@warkypublic/oranguru'; function ServerSideExample() { const [value, setValue] = useState(null); const handleAPICall = async ({ page, pageSize, search }) => { const response = await fetch(\`/api/items?page=\${page}&size=\${pageSize}&search=\${search}\`); const result = await response.json(); return { data: result.items, total: result.total }; }; return ( ); }` }, exports: ['Boxer', 'BoxerProvider', 'useBoxerStore'], hook: 'useBoxerStore()', name: 'Boxer', provider: 'BoxerProvider' }, ErrorBoundary: { components: ['ReactErrorBoundary', 'ReactBasicErrorBoundary'], description: 'React error boundary components for graceful error handling', examples: { basic: `import { ReactBasicErrorBoundary } from '@warkypublic/oranguru'; function App() { return ( ); }`, full: `import { ReactErrorBoundary } from '@warkypublic/oranguru'; function App() { const handleReportError = () => { console.log('Report error to support'); }; const handleReset = () => { console.log('Reset application state'); window.location.reload(); }; const handleRetry = () => { console.log('Retry failed operation'); }; return ( ); }`, nested: `import { ReactErrorBoundary } from '@warkypublic/oranguru'; // Multiple error boundaries for granular error handling function App() { return (
); }`, globalConfig: `import { SetErrorBoundaryOptions } from '@warkypublic/oranguru'; // Configure error boundary globally SetErrorBoundaryOptions({ disabled: false, // Set to true to pass through errors (dev mode) onError: (error, errorInfo) => { console.error('Global error handler:', error); // Send to analytics service analytics.trackError(error, errorInfo); } });` }, exports: ['ReactErrorBoundary', 'ReactBasicErrorBoundary', 'SetErrorBoundaryOptions', 'GetErrorBoundaryOptions'], name: 'ErrorBoundary' }, Former: { component: 'Former', description: 'Form component with React Hook Form integration and validation', examples: { basic: `import { Former } from '@warkypublic/oranguru'; import { useRef } from 'react'; import { Controller } from 'react-hook-form'; function BasicForm() { const formRef = useRef(null); return ( { console.log('Saving:', data); await fetch('/api/save', { method: 'POST', body: JSON.stringify(data) }); }} > ( )} rules={{ required: 'Name is required' }} /> ( )} rules={{ required: 'Email is required' }} /> ); }`, withWrapper: `import { Former, FormerModel } from '@warkypublic/oranguru'; import { useState } from 'react'; function ModalForm() { const [opened, setOpened] = useState(false); return ( <> setOpened(false)} > } /> ); }`, withAPI: `import { Former, FormerRestHeadSpecAPI } from '@warkypublic/oranguru'; function APIForm() { return ( {/* Form fields */} ); }`, customLayout: `import { Former } from '@warkypublic/oranguru'; function CustomLayoutForm() { return ( {/* Form fields */} ); }`, refMethods: `import { Former } from '@warkypublic/oranguru'; import { useRef } from 'react'; function FormWithRef() { const formRef = useRef(null); const handleValidate = async () => { const isValid = await formRef.current?.validate(); console.log('Form valid:', isValid); }; const handleSave = async () => { const result = await formRef.current?.save(); console.log('Save result:', result); }; const handleReset = () => { formRef.current?.reset(); }; return (
{/* Form fields */}
); }` }, exports: ['Former', 'FormerDialog', 'FormerModel', 'FormerPopover', 'FormerRestHeadSpecAPI'], name: 'Former', wrappers: ['FormerDialog', 'FormerModel', 'FormerPopover'] }, FormerControllers: { controls: ['TextInputCtrl', 'PasswordInputCtrl', 'NativeSelectCtrl', 'TextAreaCtrl', 'SwitchCtrl', 'ButtonCtrl', 'IconButtonCtrl'], description: 'Pre-built form input controls for use with Former', examples: { basic: `import { TextInputCtrl, PasswordInputCtrl, NativeSelectCtrl, ButtonCtrl } from '@warkypublic/oranguru'; Save ` }, exports: ['TextInputCtrl', 'PasswordInputCtrl', 'NativeSelectCtrl', 'TextAreaCtrl', 'SwitchCtrl', 'ButtonCtrl', 'IconButtonCtrl'], name: 'FormerControllers' }, GlobalStateStore: { description: 'Zustand-based global state management with automatic persistence', examples: { basic: `import { useGlobalStateStore } from '@warkypublic/oranguru'; function MyComponent() { const state = useGlobalStateStore(); return (

{state.program.name}

User: {state.user.username}

Email: {state.user.email}

Connected: {state.session.connected ? 'Yes' : 'No'}

); }`, provider: `import { GlobalStateStoreProvider } from '@warkypublic/oranguru'; function App() { return ( ); }`, stateUpdates: `import { useGlobalStateStore } from '@warkypublic/oranguru'; function StateControls() { const state = useGlobalStateStore(); const handleUpdateProgram = () => { state.setProgram({ name: 'My App', slug: 'my-app', description: 'A great application' }); }; const handleUpdateUser = () => { state.setUser({ username: 'john_doe', email: 'john@example.com', fullNames: 'John Doe' }); }; const handleToggleTheme = () => { state.setUser({ theme: { ...state.user.theme, darkMode: !state.user.theme?.darkMode } }); }; return (
); }`, layout: `import { useGlobalStateStore } from '@warkypublic/oranguru'; function LayoutControls() { const state = useGlobalStateStore(); return (
); }`, outsideReact: `import { GlobalStateStore } from '@warkypublic/oranguru'; // Access state outside React components const currentState = GlobalStateStore.getState(); console.log('API URL:', currentState.session.apiURL); // Update state outside React GlobalStateStore.getState().setAuthToken('new-token'); GlobalStateStore.getState().setApiURL('https://new-api.com'); // Subscribe to changes const unsubscribe = GlobalStateStore.subscribe( (state) => state.session.connected, (connected) => console.log('Connected:', connected) );` }, exports: ['GlobalStateStore', 'GlobalStateStoreProvider', 'useGlobalStateStore', 'useGlobalStateStoreContext'], hook: 'useGlobalStateStore()', name: 'GlobalStateStore', provider: 'GlobalStateStoreProvider', store: 'GlobalStateStore' }, Gridler: { adaptors: ['LocalDataAdaptor', 'APIAdaptorForGoLangv2', 'FormAdaptor'], component: 'Gridler', description: 'Powerful data grid component with sorting, filtering, and pagination', examples: { basic: `import { Gridler } from '@warkypublic/oranguru'; const columns = [ { id: 'name', title: 'Name', width: 200 }, { id: 'email', title: 'Email', width: 250 }, { id: 'role', title: 'Role', width: 150 } ]; const data = [ { name: 'John Doe', email: 'john@example.com', role: 'Admin' }, { name: 'Jane Smith', email: 'jane@example.com', role: 'User' } ]; function GridExample() { return ( ); }`, customCell: `// Custom cell rendering with icons const columns = [ { id: 'status', title: 'Status', width: 100, Cell: (row) => { const icon = row?.active ? '✅' : '❌'; return { data: \`\${icon} \${row?.status}\`, displayData: \`\${icon} \${row?.status}\` }; } }, { id: 'name', title: 'Name', width: 200 } ];`, api: `import { Gridler, GlidlerAPIAdaptorForGoLangv2 } from '@warkypublic/oranguru'; import { useRef } from 'react'; function APIGridExample() { const gridRef = useRef(null); const columns = [ { id: 'id', title: 'ID', width: 100 }, { id: 'name', title: 'Name', width: 200 }, { id: 'status', title: 'Status', width: 150 } ]; return ( console.log('Selected:', values)} > ); }`, withForm: `// Gridler with Former integration for inline editing import { Gridler, GlidlerAPIAdaptorForGoLangv2 } from '@warkypublic/oranguru'; import { FormerDialog } from '@warkypublic/oranguru'; import { TextInputCtrl, NativeSelectCtrl } from '@warkypublic/oranguru'; import { useState, useRef } from 'react'; function EditableGrid() { const gridRef = useRef(null); const [formProps, setFormProps] = useState({ opened: false, request: null, values: null, onClose: () => setFormProps(prev => ({ ...prev, opened: false, request: null, values: null })), onChange: (request, data) => { gridRef.current?.refresh({ value: data }); } }); const columns = [ { id: 'id', title: 'ID', width: 100 }, { id: 'name', title: 'Name', width: 200 }, { id: 'type', title: 'Type', width: 150 } ]; return ( <> { setFormProps(prev => ({ ...prev, opened: true, request: request, values: data })); }} /> ); }`, refMethods: `// Using Gridler ref methods for programmatic control import { Gridler } from '@warkypublic/oranguru'; import { useRef } from 'react'; function GridWithControls() { const gridRef = useRef(null); return ( <>
); }`, sections: `// Gridler with custom side sections import { Gridler } from '@warkypublic/oranguru'; import { useState } from 'react'; function GridWithSections() { const [sections, setSections] = useState({ top:
Top Bar
, bottom:
Bottom Bar
, left:
L
, right:
R
}); return ( ); }` }, exports: ['Gridler', 'GlidlerLocalDataAdaptor', 'GlidlerAPIAdaptorForGoLangv2', 'GlidlerFormAdaptor'], hook: 'useGridlerStore()', name: 'Gridler' }, MantineBetterMenu: { description: 'Enhanced context menus with better positioning and visibility control', examples: { provider: `import { MantineBetterMenusProvider } from '@warkypublic/oranguru'; import { MantineProvider } from '@mantine/core'; function App() { return ( ); }`, contextMenu: `import { useMantineBetterMenus } from '@warkypublic/oranguru'; function MyComponent() { const { show, hide } = useMantineBetterMenus(); const handleContextMenu = (e) => { e.preventDefault(); show('context-menu', { x: e.clientX, y: e.clientY, items: [ { label: 'Edit', onClick: () => console.log('Edit clicked') }, { label: 'Copy', onClick: () => console.log('Copy clicked') }, { isDivider: true }, { label: 'Delete', onClick: () => console.log('Delete clicked'), color: 'red' } ] }); }; return (
Right-click me for a context menu
); }`, asyncActions: `import { useMantineBetterMenus } from '@warkypublic/oranguru'; function AsyncMenuExample() { const { show } = useMantineBetterMenus(); const handleClick = (e) => { show('async-menu', { x: e.clientX, y: e.clientY, items: [ { label: 'Save', onClickAsync: async () => { await fetch('/api/save', { method: 'POST' }); console.log('Saved successfully'); } }, { label: 'Load Data', onClickAsync: async () => { const data = await fetch('/api/data').then(r => r.json()); console.log('Data loaded:', data); } } ] }); }; return ; }`, customRenderer: `import { useMantineBetterMenus } from '@warkypublic/oranguru'; function CustomMenuExample() { const { show } = useMantineBetterMenus(); const handleClick = (e) => { show('custom-menu', { x: e.clientX, y: e.clientY, items: [ { renderer: ({ loading }) => (
{loading ? 'Processing...' : 'Custom Item'}
) } ] }); }; return ; }` }, exports: ['MantineBetterMenusProvider', 'useMantineBetterMenus'], hook: 'useMantineBetterMenus()', name: 'MantineBetterMenu', provider: 'MantineBetterMenusProvider' } }; class OranguruMCPServer { constructor() { this.server = new Server( { name: 'oranguru-docs', version: '0.0.31', }, { capabilities: { resources: {}, tools: {}, }, } ); this.setupHandlers(); this.server.onerror = (error) => console.error('[MCP Error]', error); process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Oranguru MCP server running on stdio'); } setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { description: 'Get documentation for a specific Oranguru component', inputSchema: { properties: { component: { description: 'The component name', enum: Object.keys(COMPONENTS), type: 'string', }, }, required: ['component'], type: 'object', }, name: 'get_component_docs', }, { description: 'Generate code example for a specific Oranguru component', inputSchema: { properties: { component: { description: 'The component name', enum: Object.keys(COMPONENTS), type: 'string', }, variant: { description: "Example variant (e.g., 'basic', 'local', 'server')", type: 'string', }, }, required: ['component'], type: 'object', }, name: 'get_component_example', }, { description: 'List all available Oranguru components', inputSchema: { properties: {}, type: 'object', }, name: 'list_components', }, ], })); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { arguments: args, name } = request.params; switch (name) { case 'get_component_docs': if (!args.component || !COMPONENTS[args.component]) { throw new Error(`Component ${args.component} not found`); } return { content: [ { text: JSON.stringify(COMPONENTS[args.component], null, 2), type: 'text', }, ], }; case 'get_component_example': if (!args.component || !COMPONENTS[args.component]) { throw new Error(`Component ${args.component} not found`); } const component = COMPONENTS[args.component]; const variant = args.variant || Object.keys(component.examples)[0]; const example = component.examples[variant]; if (!example) { throw new Error(`Variant ${variant} not found for ${args.component}`); } return { content: [ { text: example, type: 'text', }, ], }; case 'list_components': return { content: [ { text: JSON.stringify( Object.entries(COMPONENTS).map(([key, comp]) => ({ description: comp.description, exports: comp.exports, name: key, })), null, 2 ), type: 'text', }, ], }; default: throw new Error(`Unknown tool: ${name}`); } }); // List resources this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [ { description: 'Main documentation for the Oranguru library', mimeType: 'text/markdown', name: 'Oranguru Documentation', uri: 'oranguru://docs/readme', }, { description: 'List of all available components', mimeType: 'application/json', name: 'Component List', uri: 'oranguru://docs/components', }, ], })); // Read resources this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; if (uri === 'oranguru://docs/readme') { const readmePath = join(__dirname, '..', 'README.md'); const readme = readFileSync(readmePath, 'utf-8'); return { contents: [ { mimeType: 'text/markdown', text: readme, uri, }, ], }; } if (uri === 'oranguru://docs/components') { return { contents: [ { mimeType: 'application/json', text: JSON.stringify( Object.entries(COMPONENTS).map(([key, comp]) => ({ description: comp.description, exports: comp.exports, name: key, })), null, 2 ), uri, }, ], }; } throw new Error(`Resource not found: ${uri}`); }); } } const server = new OranguruMCPServer(); server.run().catch(console.error);