Build LLM-agnostic conversational apps with custom UI components and MCP (Model Context Protocol) servers.
The Open Apps SDK is a framework for building rich, interactive applications that work with any LLM (Claude, GPT, Gemini, etc.) while maintaining full control over your UI components and data.
- β LLM-Agnostic: Works with Claude, GPT, Gemini, or any other LLM
- β Custom UI Components: Build and own your React components
- β MCP Integration: Connect to multiple MCP servers
- β Flexible Loading: Components via registry, URL, or bundled code
- β Type-Safe: Full TypeScript support
- β Bun-Powered: Fast builds and hot reloading
# Clone the repository
git clone https://github.com/maneeshsandra/open-apps-sdk.git
cd open-apps-sdk
# Run setup script (installs dependencies and configures environment)
bun run setup
# Start development server
bun run devThe application is configured through two main files:
.env- Environment variables for LLM provider, API keys, etc.mcp.config.json- MCP server configurations
# LLM Configuration
LLM_PROVIDER=lmstudio # or openai, anthropic, etc.
LLM_API_KEY=your_api_key_here
LLM_BASE_URL=http://127.0.0.1:1234/v1 # For LM Studio
LLM_MODEL=qwen/qwen3-14b
LLM_MAX_TOKENS=4096
LLM_TEMPERATURE=0.7
# Server Configuration
PORT=3000{
"mcpServers": {
"weather-server": {
"command": "bun",
"args": ["examples/mcp-servers/weather-server.ts"],
"transport": "stdio"
},
"ecommerce-server": {
"command": "bun",
"args": ["examples/mcp-servers/ecommerce-server.ts"],
"transport": "stdio"
}
}
}Update components.config.js to register your custom components:
import { MyComponent } from './components/MyComponent';
export const components = {
'my-component': {
component: MyComponent,
tools: ['my_tool_name']
}
};// components/MyComponent.tsx
import { useComponentContext, useCallTool, useToolOutput } from 'open-apps-sdk';
export function MyComponent() {
const { toolOutput, callTool, theme } = useComponentContext();
const output = useToolOutput(); // Alternative hook
const callToolFn = useCallTool(); // Alternative hook
const handleAction = async () => {
// Call MCP tool with authentication headers
await callTool('my_tool', {
param: 'value'
}, {
'Authorization': 'Bearer token',
'X-API-Key': 'api-key'
});
};
return (
<div className={`my-component ${theme}`}>
<h2>{toolOutput?.title || output?.title}</h2>
<button onClick={handleAction}>
Call Tool
</button>
</div>
);
}// examples/mcp-servers/my-server.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
const server = new Server({
name: 'my-server',
version: '1.0.0',
}, {
capabilities: { tools: {} }
});
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: 'my_tool',
description: 'Description of what this tool does',
inputSchema: {
type: 'object',
properties: {
param: { type: 'string' }
},
required: ['param']
},
_meta: {
componentId: 'my-component', // Links to your React component
componentAccessible: true
}
}]
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'my_tool') {
// Process the tool call
const result = { title: 'Tool Result', data: args };
return {
content: [{ type: 'text', text: 'Tool executed successfully' }],
structuredContent: result, // Passed to component via useToolOutput
_meta: {
componentId: 'my-component'
}
};
}
});
// Start the server
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);A full-featured shopping experience with product browsing, cart management, and checkout.
# Start the e-commerce MCP server
bun run examples/mcp-servers/ecommerce-server.tsFeatures:
- Product catalog with categories
- Shopping cart CRUD operations
- User authentication
- Order management
Our SDK provides powerful hooks for building interactive components. See detailed hooks documentation for comprehensive examples and best practices.
// All-in-one context hook
const {
toolInput, // Arguments passed to the tool
toolOutput, // Structured data from tool responses
componentState, // Persistent component state
theme, // Current theme ('light' | 'dark')
displayMode, // Display mode ('inline' | 'modal' | etc.)
callTool, // Call MCP tools with optional auth headers
setComponentState, // Update persistent state
sendMessage, // Send followup messages to conversation
} = useComponentContext();
// Individual hooks for specific needs
const input = useToolInput(); // Get tool arguments
const output = useToolOutput(); // Get tool response data
const [state, setState] = useComponentState(); // Manage state
const theme = useTheme(); // Get current theme
const callTool = useCallTool(); // Call tools with authPass authentication headers when calling tools:
const callTool = useCallTool();
await callTool('authenticated_tool', {
param: 'value'
}, {
'Authorization': 'Bearer your-token',
'X-API-Key': 'your-api-key',
'Content-Type': 'application/json'
});Components can maintain persistent state across interactions:
const [cartItems, setCartItems] = useComponentState([]);
const addToCart = (item) => {
setCartItems(prev => [...prev, item]);
};We welcome contributions! Please read our contributing guidelines before getting started.
Quick Start:
- Understand the project intent
- Create an issue first
- Create a feature branch
- Make your changes
- Submit a pull request
See TROUBLESHOOTING.md for common issues and solutions.
MIT License - see LICENSE for details.
Built with β€οΈ by the Open Apps SDK team


