Skip to content

Commit 9a9a1a4

Browse files
committed
feat: add MCP server for AI agent integration
Add HTTP-based MCP server on port 34438 that exposes Effect DevTools data to AI agents via Model Context Protocol. Tools available: - list_spans: Query spans with filtering by status, traceId, name - get_span: Get detailed span info including events - get_active_spans: Get currently running spans - list_clients: List connected Effect applications - get_metrics: Query metrics with filtering - get_span_tree: Get hierarchical span tree for a trace Implementation uses @effect/ai McpServer with BunHttpServer, running separately from the WebSocket DevTools server (port 34437).
1 parent 6a3ef3e commit 9a9a1a4

File tree

7 files changed

+564
-12
lines changed

7 files changed

+564
-12
lines changed

bun.lock

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
"": {
66
"name": "effect-devtui",
77
"dependencies": {
8+
"@effect/ai": "^0.32.1",
89
"@effect/experimental": "^0.57.11",
910
"@effect/language-service": "^0.62.1",
1011
"@effect/platform": "^0.93.6",
1112
"@effect/platform-bun": "^0.86.0",
1213
"@effect/platform-node": "^0.103.0",
14+
"@effect/rpc": "^0.72.2",
1315
"@opentui/core": "^0.1.60",
1416
"@opentui/solid": "^0.1.60",
1517
"dagre": "^0.8.5",
@@ -89,11 +91,13 @@
8991

9092
"@dimforge/rapier2d-simd-compat": ["@dimforge/[email protected]", "", {}, "sha512-bijvwWz6NHsNj5e5i1vtd3dU2pDhthSaTUZSh14DUGGKJfw8eMnlWZsxwHBxB/a3AXVNDjL9abuHw1k9FGR+jg=="],
9193

94+
"@effect/ai": ["@effect/[email protected]", "", { "dependencies": { "find-my-way-ts": "^0.1.6" }, "peerDependencies": { "@effect/experimental": "^0.57.0", "@effect/platform": "^0.93.0", "@effect/rpc": "^0.72.1", "effect": "^3.19.3" } }, "sha512-6qedQhnHigkRjNsUGaXKqvsbwhvQH3Ez3aePeROBRJi0c8q9tnR1E8qhQrTSjVPWcrv9AFtP0NRTr6iAHujGWQ=="],
95+
9296
"@effect/cluster": ["@effect/[email protected]", "", { "dependencies": { "kubernetes-types": "^1.30.0" }, "peerDependencies": { "@effect/platform": "^0.93.6", "@effect/rpc": "^0.72.2", "@effect/sql": "^0.48.6", "@effect/workflow": "^0.15.0", "effect": "^3.19.8" } }, "sha512-WeLrGpdtWhr4ap3kcW+G+qYyDqzEa65ypPWsU+sNU2gordENsAkxUQqo8taKr114AOk6WE74q0I7iWABcfGlhQ=="],
9397

9498
"@effect/experimental": ["@effect/[email protected]", "", { "dependencies": { "uuid": "^11.0.3" }, "peerDependencies": { "@effect/platform": "^0.93.6", "effect": "^3.19.9", "ioredis": "^5", "lmdb": "^3" }, "optionalPeers": ["ioredis", "lmdb"] }, "sha512-M5uug3Drs/gyTHLfA+XzcIZQGUEV/Jn5yi1POki4oZswhpzNmsVTHl4THpxAordRKwa5lFvTSlsRP684YH7pSw=="],
9599

96-
"@effect/language-service": ["@effect/[email protected].1", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-K78mPtNpHqArLTXpYNASoqiqYUmPASrdnntSBVInMYmI39hyLkmTqk78bclzkfsIvJZBBS4vvsuWx7Etmz8rkA=="],
100+
"@effect/language-service": ["@effect/[email protected].3", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-YL+YI2BbMcnSUtgESHrXWChByQH7SMhQ5DoQVhXQo2ahY9/NQGSjJ59S9Hizlfe4VneIcmpk2FkCrUZ9VwyA2Q=="],
97101

98102
"@effect/platform": ["@effect/[email protected]", "", { "dependencies": { "find-my-way-ts": "^0.1.6", "msgpackr": "^1.11.4", "multipasta": "^0.2.7" }, "peerDependencies": { "effect": "^3.19.12" } }, "sha512-xTEy6fyTy4ijmFC3afKgtvYtn/JyPoIov4ZSUWJZUv3VeOcUPNGrrqG6IJlWkXs3NhvSywKv7wc1kw3epCQVZw=="],
99103

@@ -281,7 +285,7 @@
281285

282286
"@parcel/watcher-win32-x64": ["@parcel/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="],
283287

284-
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
288+
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
285289

286290
"@tokenizer/token": ["@tokenizer/[email protected]", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
287291

@@ -313,7 +317,7 @@
313317

314318
"base64-js": ["[email protected]", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
315319

316-
"baseline-browser-mapping": ["[email protected].7", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg=="],
320+
"baseline-browser-mapping": ["[email protected].8", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Y1fOuNDowLfgKOypdc9SPABfoWXuZHBOyCS4cD52IeZBhr4Md6CLLs6atcxVrzRmQ06E7hSlm5bHHApPKR/byA=="],
317321

318322
"bmp-ts": ["[email protected]", "", {}, "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw=="],
319323

@@ -443,7 +447,7 @@
443447

444448
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
445449

446-
"msgpackr": ["[email protected].5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="],
450+
"msgpackr": ["[email protected].6", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-DzHs4d3a2AaIF4bQZNX5z7NfcoV1pHK/FIcX8Ow65s2DVYPVhJoq0S7wf6I17NkYuWRnG0mvRv4/Bii0D1PEfA=="],
447451

448452
"msgpackr-extract": ["[email protected]", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
449453

@@ -555,7 +559,7 @@
555559

556560
"undici-types": ["[email protected]", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
557561

558-
"update-browserslist-db": ["[email protected].2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="],
562+
"update-browserslist-db": ["[email protected].3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
559563

560564
"utif2": ["[email protected]", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="],
561565

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@
3636
"client": "bun example/client.ts"
3737
},
3838
"dependencies": {
39+
"@effect/ai": "^0.32.1",
3940
"@effect/experimental": "^0.57.11",
4041
"@effect/language-service": "^0.62.1",
4142
"@effect/platform": "^0.93.6",
4243
"@effect/platform-bun": "^0.86.0",
4344
"@effect/platform-node": "^0.103.0",
45+
"@effect/rpc": "^0.72.2",
4446
"@opentui/core": "^0.1.60",
4547
"@opentui/solid": "^0.1.60",
4648
"dagre": "^0.8.5",

src/mcp/server.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* MCP Server Integration
3+
*
4+
* This module creates the MCP server layer using @effect/ai's McpServer.
5+
* It registers the DevTools toolkit and provides the StoreReader dependency.
6+
*/
7+
8+
import * as McpServer from "@effect/ai/McpServer";
9+
import { HttpRouter, HttpServer, HttpMiddleware } from "@effect/platform";
10+
import { BunHttpServer } from "@effect/platform-bun";
11+
import * as Layer from "effect/Layer";
12+
import * as Effect from "effect/Effect";
13+
import { DevToolsToolkit, DevToolsToolkitHandlers } from "./tools";
14+
import { makeStoreReaderLayer } from "../storeReaderService";
15+
import type { StoreState } from "../storeTypes";
16+
17+
/**
18+
* MCP server configuration
19+
*/
20+
export const MCP_PORT = 34438;
21+
export const MCP_PATH = "/mcp";
22+
23+
/**
24+
* Create the MCP server layer.
25+
*
26+
* This layer:
27+
* 1. Creates an HTTP server on a separate port
28+
* 2. Creates an MCP server at the specified path
29+
* 3. Registers the DevTools toolkit with all tools
30+
* 4. Provides the StoreReader service for tool handlers
31+
*
32+
* @param getStore - Function to get the current store state
33+
*/
34+
export const makeMcpLayer = (getStore: () => StoreState) => {
35+
// Create MCP toolkit layer that registers tools with the server
36+
const ToolkitLayer = McpServer.toolkit(DevToolsToolkit);
37+
38+
// MCP layer with toolkit registration
39+
const McpLive = Layer.mergeAll(
40+
// The MCP HTTP layer creates the server and registers routes on HttpRouter.Default
41+
McpServer.layerHttp({
42+
name: "effect-devtools",
43+
version: "1.0.0",
44+
path: MCP_PATH,
45+
}),
46+
// Register our toolkit with the server
47+
ToolkitLayer,
48+
).pipe(
49+
// Provide the handlers for our toolkit
50+
Layer.provide(DevToolsToolkitHandlers),
51+
// Provide the store reader so handlers can access state
52+
Layer.provide(makeStoreReaderLayer(getStore)),
53+
);
54+
55+
// HTTP server layer using Bun
56+
const HttpServerLive = BunHttpServer.layer({ port: MCP_PORT });
57+
58+
// Combine everything: serve the router with MCP routes
59+
return HttpRouter.Default.unwrap(
60+
HttpServer.serve(HttpMiddleware.logger),
61+
).pipe(
62+
HttpServer.withLogAddress,
63+
Layer.provide(McpLive),
64+
Layer.provide(HttpServerLive),
65+
);
66+
};
67+
68+
/**
69+
* Run the MCP server as an Effect (for use in forked fiber)
70+
*/
71+
export const runMcpServer = (getStore: () => StoreState) =>
72+
Effect.gen(function* () {
73+
console.log(`[MCP] Starting MCP server on port ${MCP_PORT}...`);
74+
yield* Layer.launch(makeMcpLayer(getStore));
75+
});

0 commit comments

Comments
 (0)