Skip to content

Commit f03d1c6

Browse files
committed
Merge branch 'ochafik/kotlin-sdk' of https://github.com/modelcontextprotocol/ext-apps into ochafik/kotlin-sdk
2 parents 7dc8540 + cb70420 commit f03d1c6

File tree

12 files changed

+3799
-2886
lines changed

12 files changed

+3799
-2886
lines changed

examples/run-all.ts

Lines changed: 27 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*/
1010

1111
import { readdirSync, statSync, existsSync } from "fs";
12-
import { spawn, type ChildProcess } from "child_process";
12+
import concurrently from "concurrently";
1313

1414
const BASE_PORT = 3101;
1515
const BASIC_HOST = "basic-host";
@@ -36,57 +36,9 @@ const command = process.argv[2];
3636

3737
if (!command || !COMMANDS.includes(command)) {
3838
console.error(`Usage: bun examples/run-all.ts <${COMMANDS.join("|")}>`);
39-
4039
process.exit(1);
4140
}
4241

43-
const processes: ChildProcess[] = [];
44-
45-
// Handle cleanup on exit
46-
function cleanup() {
47-
for (const proc of processes) {
48-
proc.kill();
49-
}
50-
}
51-
process.on("SIGINT", cleanup);
52-
process.on("SIGTERM", cleanup);
53-
54-
// Spawn a process and track it
55-
function spawnProcess(
56-
cmd: string,
57-
args: string[],
58-
env: Record<string, string> = {},
59-
prefix: string,
60-
): ChildProcess {
61-
const proc = spawn(cmd, args, {
62-
env: { ...process.env, ...env },
63-
stdio: ["ignore", "pipe", "pipe"],
64-
});
65-
66-
proc.stdout?.on("data", (data) => {
67-
const lines = data.toString().trim().split("\n");
68-
for (const line of lines) {
69-
console.log(`[${prefix}] ${line}`);
70-
}
71-
});
72-
73-
proc.stderr?.on("data", (data) => {
74-
const lines = data.toString().trim().split("\n");
75-
for (const line of lines) {
76-
console.error(`[${prefix}] ${line}`);
77-
}
78-
});
79-
80-
proc.on("exit", (code) => {
81-
if (code !== 0 && code !== null) {
82-
console.error(`[${prefix}] exited with code ${code}`);
83-
}
84-
});
85-
86-
processes.push(proc);
87-
return proc;
88-
}
89-
9042
// Build the SERVERS environment variable (JSON array of URLs)
9143
const serversEnv = JSON.stringify(servers.map((s) => s.url));
9244

@@ -96,25 +48,34 @@ console.log(
9648
);
9749
console.log("");
9850

51+
// Build command list for concurrently
52+
const commands: Parameters<typeof concurrently>[0] = [
53+
// Server examples
54+
...servers.map(({ dir, port }) => ({
55+
command: `npm run --workspace examples/${dir} ${command}`,
56+
name: dir,
57+
env: { PORT: String(port) },
58+
})),
59+
// Basic host with SERVERS env
60+
{
61+
command: `npm run --workspace examples/${BASIC_HOST} ${command}`,
62+
name: BASIC_HOST,
63+
env: { SERVERS: serversEnv },
64+
},
65+
];
66+
9967
// If dev mode, also run the main library watcher
10068
if (command === "dev") {
101-
spawnProcess("npm", ["run", "watch"], {}, "lib");
69+
commands.unshift({
70+
command: "npm run watch",
71+
name: "lib",
72+
});
10273
}
10374

104-
// Run each server example
105-
for (const { dir, port } of servers) {
106-
spawnProcess(
107-
"npm",
108-
["run", "--workspace", `examples/${dir}`, command],
109-
{ PORT: String(port) },
110-
dir,
111-
);
112-
}
75+
const { result } = concurrently(commands, {
76+
prefix: "name",
77+
// For build command, we want all to complete; for start/dev, kill all on failure
78+
killOthersOnFail: command !== "build",
79+
});
11380

114-
// Run basic-host with the SERVERS env var
115-
spawnProcess(
116-
"npm",
117-
["run", "--workspace", `examples/${BASIC_HOST}`, command],
118-
{ SERVERS: serversEnv },
119-
BASIC_HOST,
120-
);
81+
result.catch(() => process.exit(1));
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Example: Wiki Explorer
2+
3+
Visualizes Wikipedia link graphs using a force-directed layout. Explore how Wikipedia pages are connected by expanding nodes to reveal first-degree links.
4+
5+
<table>
6+
<tr>
7+
<td><a href="https://modelcontextprotocol.github.io/ext-apps/screenshots/wiki-explorer-server/01-zoomed.png"><img src="https://modelcontextprotocol.github.io/ext-apps/screenshots/wiki-explorer-server/01-zoomed.png" alt="Zoomed" width="100%"></a></td>
8+
<td><a href="https://modelcontextprotocol.github.io/ext-apps/screenshots/wiki-explorer-server/02-pop-up.png"><img src="https://modelcontextprotocol.github.io/ext-apps/screenshots/wiki-explorer-server/02-pop-up.png" alt="Pop-up" width="100%"></a></td>
9+
<td><a href="https://modelcontextprotocol.github.io/ext-apps/screenshots/wiki-explorer-server/03-expanded-graph.png"><img src="https://modelcontextprotocol.github.io/ext-apps/screenshots/wiki-explorer-server/03-expanded-graph.png" alt="Expanded graph" width="100%"></a></td>
10+
</tr>
11+
</table>
12+
13+
## Features
14+
15+
- **Force-directed graph visualization**: Interactive graph powered by [`force-graph`](https://github.com/vasturiano/force-graph)
16+
- **Node expansion**: Click any node to expand and see all pages it links to
17+
- **Visual state tracking**: Nodes change color based on state (blue = default, green = expanded, red = error)
18+
- **Direct page access**: Open any Wikipedia page in your browser
19+
20+
## Running
21+
22+
1. Install dependencies:
23+
24+
```bash
25+
npm install
26+
```
27+
28+
2. Build and start the server:
29+
30+
```bash
31+
npm run start:http # for Streamable HTTP transport
32+
# OR
33+
npm run start:stdio # for stdio transport
34+
```
35+
36+
3. View using the [`basic-host`](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) example or another MCP Apps-compatible host.
37+
38+
### Tool Input
39+
40+
To test the example, call the `get-first-degree-links` tool with a Wikipedia URL:
41+
42+
```json
43+
{
44+
"url": "https://en.wikipedia.org/wiki/Graph_theory"
45+
}
46+
```
47+
48+
Click nodes in the graph to **Open** (view in browser) or **Expand** (visualize linked pages).
49+
50+
## Architecture
51+
52+
### Server (`server.ts`)
53+
54+
MCP server that fetches Wikipedia pages and extracts internal links.
55+
56+
Exposes one tool:
57+
58+
- `get-first-degree-links` - Returns links to other Wikipedia pages from a given page
59+
60+
### App (`src/mcp-app.ts`)
61+
62+
Vanilla TypeScript app using force-graph for visualization that:
63+
64+
- Receives tool inputs via the MCP App SDK
65+
- Renders an interactive force-directed graph
66+
- Supports node expansion to explore link relationships
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Wiki Explorer</title>
7+
</head>
8+
<body>
9+
<div id="graph"></div>
10+
<div id="popup">
11+
<div class="popup-title"></div>
12+
<div class="popup-error"></div>
13+
<div class="popup-buttons">
14+
<button id="open-btn">Open</button>
15+
<button id="expand-btn">Expand</button>
16+
</div>
17+
</div>
18+
<div id="controls">
19+
<button id="reset-graph" title="Reset graph">&#x21BA;</button>
20+
<div id="zoom-controls">
21+
<button id="zoom-in" title="Zoom in">+</button>
22+
<button id="zoom-out" title="Zoom out"></button>
23+
</div>
24+
</div>
25+
<script type="module" src="/src/mcp-app.ts"></script>
26+
</body>
27+
</html>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "wiki-explorer-server",
3+
"version": "1.0.0",
4+
"private": true,
5+
"type": "module",
6+
"scripts": {
7+
"build": "INPUT=mcp-app.html vite build",
8+
"watch": "INPUT=mcp-app.html vite build --watch",
9+
"serve:http": "bun server.ts",
10+
"serve:stdio": "bun server.ts --stdio",
11+
"start": "npm run start:http",
12+
"start:http": "NODE_ENV=development npm run build && npm run serve:http",
13+
"start:stdio": "NODE_ENV=development npm run build && npm run serve:stdio",
14+
"dev": "NODE_ENV=development concurrently 'npm run watch' 'npm run serve:http'"
15+
},
16+
"dependencies": {
17+
"@modelcontextprotocol/ext-apps": "../..",
18+
"@modelcontextprotocol/sdk": "^1.22.0",
19+
"cheerio": "^1.0.0",
20+
"zod": "^3.25.0"
21+
},
22+
"devDependencies": {
23+
"@types/cors": "^2.8.19",
24+
"@types/express": "^5.0.0",
25+
"@types/node": "^22.0.0",
26+
"bun": "^1.3.2",
27+
"concurrently": "^9.2.1",
28+
"cors": "^2.8.5",
29+
"express": "^5.1.0",
30+
"force-graph": "^1.49.0",
31+
"typescript": "^5.9.3",
32+
"vite": "^6.0.0",
33+
"vite-plugin-singlefile": "^2.3.0"
34+
}
35+
}

0 commit comments

Comments
 (0)