Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ This project may be a poor fit if you:
## Features

- Single `createApp()` entry point to define tools and UI once
- **API Versioning**: Expose multiple API versions from a single app (e.g., `/v1/mcp`, `/v2/mcp`)
- Type-safe tool bindings with full TypeScript inference for inputs, outputs, and UI access
- Protocol abstraction so UI code works identically on both platforms
- OAuth 2.1 security with JWT validation and JWKS discovery (RFC 6750, RFC 8414)
Expand Down Expand Up @@ -181,6 +182,70 @@ export type AppTools = typeof app.tools;
export type AppClientTools = ClientToolsFromCore<AppTools>;
```

### API Versioning

Expose multiple API versions from a single application, each with its own tools and UI:

```typescript
const app = createApp({
name: "my-app",

// Shared config across all versions
config: {
cors: { origin: true },
oauth: { authorizationServer: "https://auth.example.com" },
},

// Version-specific tools and config
versions: {
v1: {
version: "1.0.0",
tools: {
greet: defineTool({
description: "Greet v1",
input: z.object({ name: z.string() }),
output: z.object({ message: z.string() }),
handler: async ({ name }) => ({ message: `Hello, ${name}!` }),
}),
},
},
v2: {
version: "2.0.0",
tools: {
greet: defineTool({
description: "Greet v2",
input: z.object({ name: z.string(), surname: z.string().optional() }),
output: z.object({ message: z.string() }),
handler: async ({ name, surname }) => ({
message: `Hello, ${name} ${surname || ""}!`.trim(),
}),
}),
},
// Version-specific config overrides global config
config: {
protocol: "openai",
},
},
},
});

// Access version info programmatically
console.log(app.getVersions()); // ["v1", "v2"]
const v2App = app.getVersion("v2");

// Each version is exposed at its dedicated route
// - v1: http://localhost:3000/v1/mcp
// - v2: http://localhost:3000/v2/mcp
```

Each version can have:

- Different tools and tool schemas
- Version-specific UI components
- Config overrides (merged with global config)
- Version-specific plugins (merged with global plugins)
- Shared middleware and OAuth configuration

### UI Setup (React)

```typescript
Expand Down Expand Up @@ -455,7 +520,7 @@ npm run dev

Local examples:

- [examples/minimal](examples/minimal/) - minimal server and UI widget
- [examples/minimal](examples/minimal/) - minimal server with API versioning (v1 and v2)
- [examples/restaurant-finder](examples/restaurant-finder/) - end-to-end app with search functionality

## API
Expand Down
104 changes: 91 additions & 13 deletions examples/minimal/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Minimal Example
# Minimal Example with Versioning

A simple "hello world" example demonstrating basic @mcp-apps-kit/core usage.
A simple example demonstrating @mcp-apps-kit/core versioning support - exposing multiple API versions from a single application.

## Features

- Single tool definition with Zod schema validation
- Simple UI widget showing greeting messages
- Basic server setup
- **API Versioning**: Two API versions exposed at different routes
- `v1`: Simple greet tool (name only)
- `v2`: Enhanced greet tool (name + optional surname)
- **Shared Configuration**: CORS, debug settings shared across versions
- **Type-Safe Tools**: Full TypeScript support for each version's tools
- **React UI Widgets**: Version-specific UI components

## Quick Start

Expand All @@ -22,16 +25,42 @@ pnpm build
pnpm start
```

## API Endpoints

Once running, the server exposes:

| Endpoint | Description |
| -------------- | --------------------------- |
| `GET /health` | Health check |
| `POST /v1/mcp` | MCP v1 API (name only) |
| `POST /v2/mcp` | MCP v2 API (name + surname) |

## Testing the API

```bash
# v1: Greet with name only
curl -X POST http://localhost:3000/v1/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"greet","arguments":{"name":"World"}},"id":1}'

# v2: Greet with name and surname
curl -X POST http://localhost:3000/v2/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"greet","arguments":{"name":"John","surname":"Doe"}},"id":1}'
```

## Connecting to Claude Desktop

Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):

```json
{
"mcpServers": {
"minimal-app": {
"command": "npx",
"args": ["tsx", "path/to/examples/minimal/src/index.ts"]
"minimal-app-v1": {
"url": "http://localhost:3000/v1/mcp"
},
"minimal-app-v2": {
"url": "http://localhost:3000/v2/mcp"
}
}
}
Expand All @@ -42,18 +71,20 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
```
minimal/
src/
index.ts # Server with tool definition
index.ts # Server with versioned app setup
ui/
index.html # Widget HTML entry
main.ts # Widget TypeScript
GreetingWidgetV1.tsx # V1 UI widget (name only)
GreetingWidgetV2.tsx # V2 UI widget (name + surname)
styles.css # Shared styles
dist/ # Built UI HTML files
package.json
tsconfig.json
vite.config.ts
```

## Tool
## Tools

### `greet`
### V1: `greet`

Greet someone by name.

Expand All @@ -65,3 +96,50 @@ Greet someone by name.

- `message` (string): Greeting message
- `timestamp` (string): ISO timestamp

### V2: `greet`

Greet someone by name and optional surname.

**Input:**

- `name` (string): First name to greet
- `surname` (string, optional): Surname

**Output:**

- `message` (string): Greeting message
- `fullName` (string): The full name used in greeting
- `timestamp` (string): ISO timestamp

## Versioning Configuration

The app uses the `createApp` versioning feature:

```typescript
const app = createApp({
name: "minimal-app",

// Shared config across all versions
config: {
cors: { origin: true },
debug: { logTool: true, level: "info" },
},

// Version-specific tools and config
versions: {
v1: {
version: "1.0.0",
tools: { greet: greetToolV1 },
},
v2: {
version: "2.0.0",
tools: { greet: greetToolV2 },
},
},
});

// Access version info programmatically
console.log(app.getVersions()); // ["v1", "v2"]
const v2App = app.getVersion("v2");
```
Loading