Features at a Glance
| Feature | Description |
|---|---|
| File-Based Development | Tools, widgets, and workflows auto-discovered from your filesystem |
| Convention over Configuration | tools/get-weather.ts → getWeather tool, automatically |
| Colocated Widgets | React UIs in ui/widgets/ auto-bind to matching tools |
| Type-Safe End-to-End | Full TypeScript inference from inputs to UI props |
| Dual Platform | Single codebase deploys to MCP Apps & ChatGPT |
| Hot Module Reload | Vite-powered HMR with React Fast Refresh for widgets |
| Zero Boilerplate | No manifest files to maintain — codegen handles it |
Quick Start
npx @mcp-apps-kit/create-app@latest my-app
cd my-app && npm run dev
Or manually set up a file-based project:
npm install @mcp-apps-kit/core @mcp-apps-kit/codegen zod
How It Works
MCP AppsKit uses file-based conventions to eliminate boilerplate. Drop files in the right directories and everything wires up automatically.
Project Structure
my-app/
├── mcp.config.ts # App configuration (single source of truth)
├── tools/
│ ├── get-weather.ts # → getWeather tool
│ └── search-location.ts # → searchLocation tool
├── workflows/
│ └── daily-briefing.ts # → dailyBriefing workflow
├── ui/widgets/
│ ├── get-weather.tsx # → Auto-bound to getWeather tool
│ └── daily-briefing.tsx # → Auto-bound to dailyBriefing
├── middleware/
│ └── logging.ts # Runs on every tool call
├── handlers/
│ └── app-lifecycle.ts # Server events (start, shutdown)
└── __generated__/ # Auto-generated (gitignored)
└── app-manifest.ts
Configuration
// mcp.config.ts
import { defineConfig } from "@mcp-apps-kit/codegen";
export default defineConfig({
name: "weather-app",
version: "1.0.0",
directories: {
tools: "tools",
workflows: "workflows",
uiWidgets: "ui/widgets",
middleware: "middleware",
handlers: "handlers",
},
config: {
protocol: "mcp", // or "openai" for ChatGPT
cors: { origin: true },
},
});
Define a Tool
// tools/get-weather.ts
import { tool } from "@mcp-apps-kit/core";
import { z } from "zod";
export default tool
.describe("Get current weather for a location")
.input({
location: z.string().describe("City name"),
})
.output(
z.object({
temperature: z.number(),
conditions: z.string(),
humidity: z.number(),
})
)
.handle(async ({ location }) => {
const data = await fetchWeather(location);
return {
temperature: data.temp,
conditions: data.weather,
humidity: data.humidity,
};
});
Create a Widget
Widgets in ui/widgets/ automatically bind to tools with matching filenames:
// ui/widgets/get-weather.tsx
import { useToolResult, useHostContext } from "@mcp-apps-kit/ui-react";
import type { WidgetMetadata } from "@mcp-apps-kit/core";
export default function WeatherWidget() {
const result = useToolResult<{ temperature: number; conditions: string }>();
const { theme } = useHostContext();
if (!result) return <div>Loading...</div>;
return (
<div className={theme === "dark" ? "dark" : ""}>
<h2>{result.temperature}°C</h2>
<p>{result.conditions}</p>
</div>
);
}
// Widget metadata (optional)
export const ui: WidgetMetadata = {
name: "Weather Display",
prefersBorder: true,
};
Run Your App
npm run dev
The codegen watches for changes and regenerates the manifest. Your tools and widgets are instantly available. Widget changes are picked up by Vite's HMR with React Fast Refresh — no full reload needed.
Naming Conventions
Files are automatically converted to camelCase identifiers:
| File | Tool Name |
|---|---|
get-current-weather.ts |
getCurrentWeather |
search_locations.ts |
searchLocations |
DailyBriefing.ts |
dailyBriefing |
_shared.ts |
(ignored — underscore prefix) |
Packages
| Package | Description |
|---|---|
@mcp-apps-kit/core |
Server framework with tool/workflow builders |
@mcp-apps-kit/codegen |
File-based discovery and manifest generation |
@mcp-apps-kit/ui |
Client SDK (vanilla JS) |
@mcp-apps-kit/ui-react |
React bindings and hooks |
@mcp-apps-kit/ui-react-builder |
Vite plugin for widget bundling and HMR |
@mcp-apps-kit/create-app |
CLI scaffolding tool |
@mcp-apps-kit/testing |
Test utilities and mocks |
Advanced Features
Middleware
Add cross-cutting concerns like logging, auth, or metrics:
// middleware/logging.ts
import { defineMiddleware } from "@mcp-apps-kit/codegen";
export default defineMiddleware({
before: async (ctx) => {
ctx.state.set("startTime", Date.now());
console.log(`Tool called: ${ctx.toolName}`);
},
after: async (ctx) => {
const duration = Date.now() - ctx.state.get("startTime");
console.log(`Completed in ${duration}ms`);
},
});
Event Handlers
React to server lifecycle events:
// handlers/app-lifecycle.ts
import { defineHandler, Events } from "@mcp-apps-kit/codegen";
export default defineHandler({
event: Events.APP_START,
handler: async ({ port }) => {
console.log(`Server started on port ${port}`);
},
});
Multi-Version APIs
Support multiple API versions from a single codebase:
// mcp.config.ts
export default defineConfig({
name: "my-api",
versions: {
v1: {
version: "1.0.0",
// Uses versions/v1/tools, versions/v1/workflows, etc.
},
v2: {
version: "2.0.0",
config: { debug: { level: "debug" } },
},
},
});
Workflow Engine
Compose multi-step operations:
// workflows/order-process.ts
import { workflow, toolStep } from "@mcp-apps-kit/core";
import { z } from "zod";
export default workflow("process_order")
.input({ orderId: z.string() })
.output({ success: z.boolean() })
.step("validate", toolStep("validate_order"))
.step("payment", toolStep("process_payment"), {
retry: { maxAttempts: 3, backoff: "exponential" },
})
.parallel("notify", [toolStep("send_email"), toolStep("send_sms")])
.build();
Platform Support
| Feature | MCP Apps | ChatGPT Apps |
|---|---|---|
| Tool Calling | Yes | Yes |
| Structured Output | Yes | Yes |
| React Widgets | Yes | Yes |
| OAuth 2.1 | Yes | Yes |
| Theme Support | Yes | Yes |
| Persisted State | No | Yes |
| Tool Cancellation | Yes | No |
Examples
Weather App
Full-featured example with tools, widgets, middleware, and workflows:
git clone https://github.com/AndurilCode/mcp-apps-kit.git
cd mcp-apps-kit/examples/weather-app
pnpm install && pnpm dev
Kanban Demo
Production-ready example with tool calling, React widgets, plugins, middleware, and events:
git clone https://github.com/AndurilCode/kanban-mcp-example.git
cd kanban-mcp-example
npm install && npm run dev
Deployment
Express (default)
// Handled automatically by codegen
npm run start
Stdio Mode
// mcp.config.ts
export default defineConfig({
// ...
config: {
transport: "stdio",
},
});
Serverless
import { createFileBasedApp } from "@mcp-apps-kit/codegen";
const app = await createFileBasedApp();
export default {
async fetch(request: Request) {
return app.handleRequest(request);
},
};
Compatibility
- Node.js: >= 18 (runtime), >= 20 (development/CLI)
- React: 18.x or 19.x
- Zod: ^4.0.0
- Vite: 5.x, 6.x, or 7.x
API Reference
Full Documentation — TypeDoc-generated API reference
Package documentation:
Contributing
See CONTRIBUTING.md for development setup. Issues and pull requests welcome.
No comments yet
Be the first to share your take.