@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.
Files changed (41) hide show
  1. package/.turbo/turbo-build.log +20 -0
  2. package/E2E_TESTING.md +198 -0
  3. package/LICENSE +373 -0
  4. package/README.md +134 -0
  5. package/dist/index.d.ts +108 -0
  6. package/dist/index.js +1592 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/server.d.ts +2 -0
  9. package/dist/server.js +1841 -0
  10. package/dist/server.js.map +1 -0
  11. package/package.json +52 -0
  12. package/src/__tests__/cache.test.ts +119 -0
  13. package/src/__tests__/cdn-plugin.test.ts +86 -0
  14. package/src/__tests__/compile.test.ts +93 -0
  15. package/src/__tests__/e2e-pipeline.test.ts +417 -0
  16. package/src/__tests__/live-update.test.ts +158 -0
  17. package/src/__tests__/local-backend.test.ts +100 -0
  18. package/src/__tests__/memory-backend.ts +144 -0
  19. package/src/__tests__/registry-backend.test.ts +256 -0
  20. package/src/__tests__/services.test.ts +153 -0
  21. package/src/__tests__/shim.test.ts +132 -0
  22. package/src/__tests__/widget-store.test.ts +183 -0
  23. package/src/compiler/cache.ts +68 -0
  24. package/src/compiler/cdn-plugin.ts +197 -0
  25. package/src/compiler/compile.ts +306 -0
  26. package/src/compiler/index.ts +3 -0
  27. package/src/hello-world.html +79 -0
  28. package/src/html.d.ts +4 -0
  29. package/src/index.ts +641 -0
  30. package/src/live-update.ts +149 -0
  31. package/src/reference-widgets/live-dashboard.ts +162 -0
  32. package/src/registry-backend.ts +304 -0
  33. package/src/server.ts +178 -0
  34. package/src/services.ts +251 -0
  35. package/src/shim.ts +247 -0
  36. package/src/widget-store/index.ts +10 -0
  37. package/src/widget-store/local-backend.ts +77 -0
  38. package/src/widget-store/store.ts +198 -0
  39. package/src/widget-store/types.ts +50 -0
  40. package/tsconfig.json +10 -0
  41. 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 |
@@ -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 };