@aprovan/mcp-app-server 0.1.0-dev.c1ac1dc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +20 -0
- package/E2E_TESTING.md +198 -0
- package/LICENSE +373 -0
- package/README.md +134 -0
- package/dist/index.d.ts +108 -0
- package/dist/index.js +1592 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +1841 -0
- package/dist/server.js.map +1 -0
- package/package.json +52 -0
- package/src/__tests__/cache.test.ts +119 -0
- package/src/__tests__/cdn-plugin.test.ts +86 -0
- package/src/__tests__/compile.test.ts +93 -0
- package/src/__tests__/e2e-pipeline.test.ts +417 -0
- package/src/__tests__/live-update.test.ts +158 -0
- package/src/__tests__/local-backend.test.ts +100 -0
- package/src/__tests__/memory-backend.ts +144 -0
- package/src/__tests__/registry-backend.test.ts +256 -0
- package/src/__tests__/services.test.ts +153 -0
- package/src/__tests__/shim.test.ts +132 -0
- package/src/__tests__/widget-store.test.ts +183 -0
- package/src/compiler/cache.ts +68 -0
- package/src/compiler/cdn-plugin.ts +197 -0
- package/src/compiler/compile.ts +306 -0
- package/src/compiler/index.ts +3 -0
- package/src/hello-world.html +79 -0
- package/src/html.d.ts +4 -0
- package/src/index.ts +641 -0
- package/src/live-update.ts +149 -0
- package/src/reference-widgets/live-dashboard.ts +162 -0
- package/src/registry-backend.ts +304 -0
- package/src/server.ts +178 -0
- package/src/services.ts +251 -0
- package/src/shim.ts +247 -0
- package/src/widget-store/index.ts +10 -0
- package/src/widget-store/local-backend.ts +77 -0
- package/src/widget-store/store.ts +198 -0
- package/src/widget-store/types.ts +50 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# @aprovan/mcp-app-server
|
|
2
|
+
|
|
3
|
+
MCP App Server — hosts MCP tools that compile and render interactive widgets in Claude Desktop and Claude web, optionally bridged to third-party APIs via the Aprovan Registry.
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm dev # start with tsx watch (hot-reload)
|
|
9
|
+
# or
|
|
10
|
+
pnpm build && pnpm start
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Server runs on `http://0.0.0.0:3000` by default.
|
|
14
|
+
|
|
15
|
+
## Registry integration (APR-61)
|
|
16
|
+
|
|
17
|
+
The server can optionally connect to the [Aprovan Registry](https://github.com/AprovanLabs/registry) MCP server to expose 30+ third-party APIs (GitHub, Slack, Stripe, Datadog, …) as callable widget services.
|
|
18
|
+
|
|
19
|
+
### Enabling the Registry backend
|
|
20
|
+
|
|
21
|
+
Set `REGISTRY_PROVIDERS` before starting the server:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
REGISTRY_PROVIDERS=github,slack,stripe pnpm dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The server will spawn `npx @utdk/mcp-server` automatically with the requested providers.
|
|
28
|
+
|
|
29
|
+
### Credential configuration
|
|
30
|
+
|
|
31
|
+
The server forwards all environment variables to the Registry child process, so you only need to set credentials once in the parent environment. Below are the most common providers:
|
|
32
|
+
|
|
33
|
+
| Provider | Required env vars | Notes |
|
|
34
|
+
|----------|-------------------|-------|
|
|
35
|
+
| **GitHub** | `GITHUB_TOKEN` | Personal access token or fine-grained token |
|
|
36
|
+
| **Stripe** | `STRIPE_SECRET_KEY` | Secret key (`sk_test_…` or `sk_live_…`) |
|
|
37
|
+
| **Slack** | `SLACK_CLIENT_ID`, `SLACK_CLIENT_SECRET` | OAuth2 client credentials; flows via client-credentials grant |
|
|
38
|
+
| **Notion** | `NOTION_API_KEY` | Integration token from Notion developer portal |
|
|
39
|
+
| **Jira** | `JIRA_EMAIL`, `JIRA_API_TOKEN` | User email + API token (basic auth) |
|
|
40
|
+
| **Datadog** | `DD_API_KEY`, `DD_APP_KEY` | API key + application key |
|
|
41
|
+
| **HubSpot** | `HUBSPOT_CLIENT_ID`, `HUBSPOT_CLIENT_SECRET` | OAuth2 client credentials |
|
|
42
|
+
| **Linear** | `LINEAR_API_KEY` | Personal API key |
|
|
43
|
+
| **Airtable** | `AIRTABLE_API_KEY` | Personal access token |
|
|
44
|
+
| **Zendesk** | `ZENDESK_CLIENT_ID`, `ZENDESK_CLIENT_SECRET` | OAuth2 client credentials |
|
|
45
|
+
| **Salesforce** | `SALESFORCE_CLIENT_ID`, `SALESFORCE_CLIENT_SECRET` | Connected app credentials |
|
|
46
|
+
| **SendGrid** | `SENDGRID_API_KEY` | API key |
|
|
47
|
+
| **Twilio** | `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN` | Account SID + auth token |
|
|
48
|
+
| **Intercom** | `INTERCOM_ACCESS_TOKEN` | Access token from Intercom developer hub |
|
|
49
|
+
| **Figma** | `FIGMA_ACCESS_TOKEN` | Personal access token |
|
|
50
|
+
| **OpenAI** | `OPENAI_API_KEY` | API key |
|
|
51
|
+
| **Discord** | `DISCORD_BOT_TOKEN` | Bot token |
|
|
52
|
+
|
|
53
|
+
For providers not listed here, inspect the provider package at `packages/utdk/<provider>/package.json` in the registry repo and look at the `utdk.auth` field.
|
|
54
|
+
|
|
55
|
+
### Local development example
|
|
56
|
+
|
|
57
|
+
Create a `.env` file (never commit this):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# .env — loaded by dotenv or direnv
|
|
61
|
+
REGISTRY_PROVIDERS=github,stripe
|
|
62
|
+
|
|
63
|
+
GITHUB_TOKEN=ghp_...
|
|
64
|
+
STRIPE_SECRET_KEY=sk_test_...
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then start:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# with dotenv-cli
|
|
71
|
+
dotenv pnpm dev
|
|
72
|
+
|
|
73
|
+
# or export manually
|
|
74
|
+
export $(grep -v '^#' .env | xargs) && pnpm dev
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Using Registry tools in a widget
|
|
78
|
+
|
|
79
|
+
Once the Registry is connected, any tool namespace (e.g. `github`, `stripe`) can be requested in the `compile_widget` `services` parameter:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Tool: compile_widget
|
|
83
|
+
{
|
|
84
|
+
"source": "...",
|
|
85
|
+
"services": ["github", "stripe"]
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Inside the widget, services are available as global namespace objects:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// List repositories
|
|
93
|
+
const repos = await github.repos_list({ per_page: 10 });
|
|
94
|
+
|
|
95
|
+
// Create a payment intent
|
|
96
|
+
const intent = await stripe.payment_intents_create({
|
|
97
|
+
amount: 2000,
|
|
98
|
+
currency: "usd",
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The call chain is: widget → `callServerTool` → `ServiceBridge` → `RegistryBackend` → `@utdk/mcp-server` → provider API.
|
|
103
|
+
|
|
104
|
+
### Overriding the Registry command
|
|
105
|
+
|
|
106
|
+
By default the server uses `npx @utdk/mcp-server`. You can point to a locally built binary instead:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
REGISTRY_COMMAND=/path/to/registry/apps/mcp-server/dist/server.js \
|
|
110
|
+
REGISTRY_PROVIDERS=github \
|
|
111
|
+
GITHUB_TOKEN=ghp_... \
|
|
112
|
+
node dist/server.js
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Namespace collision avoidance
|
|
116
|
+
|
|
117
|
+
Registry tool namespaces (one per provider) are registered separately from any locally-defined service tools. To check which namespaces are active, call `search_services` with no arguments from a Claude conversation using the connected MCP server.
|
|
118
|
+
|
|
119
|
+
## Environment variables
|
|
120
|
+
|
|
121
|
+
| Variable | Default | Description |
|
|
122
|
+
|----------|---------|-------------|
|
|
123
|
+
| `PORT` | `3000` | HTTP port to listen on |
|
|
124
|
+
| `HOST` | `0.0.0.0` | Host interface |
|
|
125
|
+
| `REGISTRY_PROVIDERS` | _(unset)_ | Enable Registry backend; comma-separated provider list |
|
|
126
|
+
| `REGISTRY_COMMAND` | `npx` | Command used to spawn the Registry MCP server |
|
|
127
|
+
| `REGISTRY_ARGS` | _(unset)_ | Space-separated extra args appended after `@utdk/mcp-server` |
|
|
128
|
+
|
|
129
|
+
## MCP endpoints
|
|
130
|
+
|
|
131
|
+
| Method | Path | Description |
|
|
132
|
+
|--------|------|-------------|
|
|
133
|
+
| `POST` | `/mcp` | MCP Streamable HTTP endpoint |
|
|
134
|
+
| `GET` | `/health` | Health check |
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { Manifest } from '@aprovan/patchwork-compiler';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A single buffered event for a data stream.
|
|
6
|
+
*/
|
|
7
|
+
interface StreamEvent {
|
|
8
|
+
seq: number;
|
|
9
|
+
data: unknown;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Push a data update to all sessions that have subscribed to `stream`.
|
|
14
|
+
*
|
|
15
|
+
* The event is appended to the ring buffer. Each subscribed session's McpServer
|
|
16
|
+
* then sends a `notifications/tools/list_changed` notification to the host.
|
|
17
|
+
* The host forwards this to the widget, which uses it as a signal to call
|
|
18
|
+
* `poll_updates` and retrieve the buffered data.
|
|
19
|
+
*
|
|
20
|
+
* @returns The new sequence number assigned to this event.
|
|
21
|
+
*/
|
|
22
|
+
declare function pushStreamUpdate(stream: string, data: unknown): Promise<number>;
|
|
23
|
+
|
|
24
|
+
interface ServiceBackend {
|
|
25
|
+
call(namespace: string, procedure: string, args: unknown[]): Promise<unknown>;
|
|
26
|
+
}
|
|
27
|
+
interface ServiceToolInfo {
|
|
28
|
+
name: string;
|
|
29
|
+
namespace: string;
|
|
30
|
+
procedure: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
parameters?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
interface ServiceBridgeConfig {
|
|
35
|
+
backend: ServiceBackend;
|
|
36
|
+
tools: ServiceToolInfo[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface FileStats {
|
|
40
|
+
size: number;
|
|
41
|
+
mtime: Date;
|
|
42
|
+
isFile(): boolean;
|
|
43
|
+
isDirectory(): boolean;
|
|
44
|
+
}
|
|
45
|
+
interface DirEntry {
|
|
46
|
+
name: string;
|
|
47
|
+
isFile(): boolean;
|
|
48
|
+
isDirectory(): boolean;
|
|
49
|
+
}
|
|
50
|
+
interface FSProvider {
|
|
51
|
+
readFile(path: string, encoding?: "utf8" | "base64"): Promise<string>;
|
|
52
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
53
|
+
unlink(path: string): Promise<void>;
|
|
54
|
+
stat(path: string): Promise<FileStats>;
|
|
55
|
+
mkdir(path: string, options?: {
|
|
56
|
+
recursive?: boolean;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
readdir(path: string): Promise<DirEntry[]>;
|
|
59
|
+
rmdir(path: string, options?: {
|
|
60
|
+
recursive?: boolean;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
exists(path: string): Promise<boolean>;
|
|
63
|
+
}
|
|
64
|
+
interface StoredWidget {
|
|
65
|
+
path: string;
|
|
66
|
+
resourceUri: string;
|
|
67
|
+
html: string;
|
|
68
|
+
manifest: Manifest;
|
|
69
|
+
entry?: string;
|
|
70
|
+
createdAt: number;
|
|
71
|
+
}
|
|
72
|
+
interface StoredWidgetInfo {
|
|
73
|
+
path: string;
|
|
74
|
+
resourceUri: string;
|
|
75
|
+
name: string;
|
|
76
|
+
version: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
services?: string[];
|
|
79
|
+
entry?: string;
|
|
80
|
+
createdAt: number;
|
|
81
|
+
}
|
|
82
|
+
interface WidgetStoreOptions {
|
|
83
|
+
storageDir?: string;
|
|
84
|
+
backend?: FSProvider;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare class WidgetStore {
|
|
88
|
+
private provider;
|
|
89
|
+
private storageDir;
|
|
90
|
+
constructor(options?: WidgetStoreOptions);
|
|
91
|
+
private fullPath;
|
|
92
|
+
saveWidget(hash: string, html: string, manifest: Manifest, entry?: string): Promise<StoredWidget>;
|
|
93
|
+
getWidget(name: string, hash: string): Promise<StoredWidget | null>;
|
|
94
|
+
listWidgets(): Promise<StoredWidgetInfo[]>;
|
|
95
|
+
deleteWidget(name: string, hash: string): Promise<boolean>;
|
|
96
|
+
hasWidget(name: string, hash: string): Promise<boolean>;
|
|
97
|
+
resourceUriFor(name: string, hash: string): string;
|
|
98
|
+
loadAll(): Promise<StoredWidget[]>;
|
|
99
|
+
}
|
|
100
|
+
declare function getWidgetStore(options?: WidgetStoreOptions): WidgetStore;
|
|
101
|
+
declare function resetWidgetStore(): void;
|
|
102
|
+
|
|
103
|
+
interface McpAppServerOptions {
|
|
104
|
+
services?: ServiceBridgeConfig;
|
|
105
|
+
}
|
|
106
|
+
declare function createMcpAppServer(options?: McpAppServerOptions): McpServer;
|
|
107
|
+
|
|
108
|
+
export { type McpAppServerOptions, type ServiceBackend, type ServiceBridgeConfig, type ServiceToolInfo, type StoredWidget, type StoredWidgetInfo, type StreamEvent, WidgetStore, type WidgetStoreOptions, createMcpAppServer, getWidgetStore, pushStreamUpdate, resetWidgetStore };
|