History

move to example

Rhys Sullivan committed 8 months ago

Commit d2e84cd1 parent 3d1a56a7 files · +13 −13Browse files

Showing the changes to apps (6 of 7 files). Show all

Unified · Split

6 files
  1. apps/mcp-elicitation-demo/CHANGELOG.md +0 −3
  2. apps/mcp-elicitation-demo/package.json +0 −21
  3. apps/mcp-elicitation-demo/src/index.ts +0 −4
  4. apps/mcp-elicitation-demo/src/main.ts +0 −20
  5. apps/mcp-elicitation-demo/src/server.ts +0 −222
  6. apps/mcp-elicitation-demo/tsconfig.json +0 −15
apps/mcp-elicitation-demo/CHANGELOG.md+0−3
@@ -1,3 +0,0 @@
1−# @executor/mcp-elicitation-demo
2−
3−## null
apps/mcp-elicitation-demo/package.json+0−21
@@ -1,21 +0,0 @@
1−{
2− "name": "@executor/mcp-elicitation-demo",
3− "private": true,
4− "type": "module",
5− "exports": {
6− ".": "./src/index.ts"
7− },
8− "scripts": {
9− "typecheck": "bunx tsc --noEmit -p tsconfig.json",
10− "start": "bun src/main.ts"
11− },
12− "dependencies": {
13− "@modelcontextprotocol/sdk": "^1.26.0",
14− "zod": "catalog:"
15− },
16− "devDependencies": {
17− "@types/node": "catalog:",
18− "bun-types": "catalog:"
19− },
20− "version": "0.0.0"
21−}
apps/mcp-elicitation-demo/src/index.ts+0−4
@@ -1,4 +0,0 @@
1−export {
2− startMcpElicitationDemoServer,
3− type McpElicitationDemoServer,
4−} from "./server";
apps/mcp-elicitation-demo/src/main.ts+0−20
@@ -1,20 +0,0 @@
1−import { startMcpElicitationDemoServer } from "./server";
2−
3−const host = process.env.HOST ?? "127.0.0.1";
4−const port = process.env.PORT ? Number(process.env.PORT) : 58506;
5−
6−const server = await startMcpElicitationDemoServer({ host, port });
7−console.error(`mcp-elicitation-demo listening on ${server.endpoint}`);
8−
9−const shutdown = async () => {
10− await server.close();
11− process.exit(0);
12−};
13−
14−process.once("SIGINT", () => {
15− void shutdown();
16−});
17−
18−process.once("SIGTERM", () => {
19− void shutdown();
20−});
apps/mcp-elicitation-demo/src/server.ts+0−222
@@ -1,222 +0,0 @@
1−import { randomUUID } from "node:crypto";
2−
3−import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
4−import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5−import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6−import { z } from "zod/v4";
7−
8−export type McpElicitationDemoServer = {
9− readonly endpoint: string;
10− readonly close: () => Promise<void>;
11−};
12−
13−const registerDemoTools = (server: McpServer) => {
14− server.registerTool(
15− "gated_echo",
16− {
17− description: "Ask for approval before echoing the provided value.",
18− inputSchema: {
19− value: z.string(),
20− },
21− },
22− async ({ value }: { value: string }) => {
23− const response = await server.server.elicitInput({
24− mode: "form",
25− message: `Approve gated echo for ${value}?`,
26− requestedSchema: {
27− type: "object",
28− properties: {
29− approve: {
30− type: "boolean",
31− title: "Approve",
32− },
33− },
34− required: ["approve"],
35− },
36− });
37−
38− if (
39− response.action !== "accept"
40− || !response.content
41− || response.content.approve !== true
42− ) {
43− return {
44− content: [{ type: "text", text: "denied" }],
45− };
46− }
47−
48− return {
49− content: [{ type: "text", text: `approved:${value}` }],
50− };
51− },
52− );
53−};
54−
55−export const startMcpElicitationDemoServer = async (input: {
56− readonly host?: string;
57− readonly port?: number;
58−} = {}): Promise<McpElicitationDemoServer> => {
59− const host = input.host ?? "127.0.0.1";
60− const app = createMcpExpressApp({ host });
61− const transports: Record<string, StreamableHTTPServerTransport> = {};
62− const servers: Record<string, McpServer> = {};
63−
64− const createServer = () => {
65− const server = new McpServer(
66− {
67− name: "executor-mcp-elicitation-demo",
68− version: "1.0.0",
69− },
70− {
71− capabilities: {
72− tools: {},
73− },
74− },
75− );
76−
77− registerDemoTools(server);
78− return server;
79− };
80−
81− app.post("/mcp", async (req: any, res: any) => {
82− const sessionIdHeader = req.headers["mcp-session-id"];
83− const sessionId =
84− typeof sessionIdHeader === "string"
85− ? sessionIdHeader
86− : Array.isArray(sessionIdHeader)
87− ? sessionIdHeader[0]
88− : undefined;
89−
90− try {
91− let transport: StreamableHTTPServerTransport;
92−
93− if (sessionId && transports[sessionId]) {
94− transport = transports[sessionId];
95− } else {
96− transport = new StreamableHTTPServerTransport({
97− sessionIdGenerator: () => randomUUID(),
98− onsessioninitialized: (newSessionId) => {
99− transports[newSessionId] = transport;
100− },
101− });
102−
103− transport.onclose = () => {
104− const closedSessionId = transport.sessionId;
105− if (closedSessionId && transports[closedSessionId]) {
106− delete transports[closedSessionId];
107− }
108− if (closedSessionId && servers[closedSessionId]) {
109− void servers[closedSessionId].close().catch(() => undefined);
110− delete servers[closedSessionId];
111− }
112− };
113−
114− const server = createServer();
115− await server.connect(transport);
116− const newSessionId = transport.sessionId;
117− if (newSessionId) {
118− servers[newSessionId] = server;
119− }
120− }
121−
122− await transport.handleRequest(req, res, req.body);
123− } catch (error) {
124− if (!res.headersSent) {
125− res.status(500).json({
126− jsonrpc: "2.0",
127− error: {
128− code: -32603,
129− message: error instanceof Error ? error.message : "Internal server error",
130− },
131− id: null,
132− });
133− }
134− }
135− });
136−
137− app.get("/mcp", async (req: any, res: any) => {
138− const sessionIdHeader = req.headers["mcp-session-id"];
139− const sessionId =
140− typeof sessionIdHeader === "string"
141− ? sessionIdHeader
142− : Array.isArray(sessionIdHeader)
143− ? sessionIdHeader[0]
144− : undefined;
145−
146− if (!sessionId || !transports[sessionId]) {
147− res.status(400).send("Invalid or missing session ID");
148− return;
149− }
150−
151− await transports[sessionId].handleRequest(req, res);
152− });
153−
154− app.delete("/mcp", async (req: any, res: any) => {
155− const sessionIdHeader = req.headers["mcp-session-id"];
156− const sessionId =
157− typeof sessionIdHeader === "string"
158− ? sessionIdHeader
159− : Array.isArray(sessionIdHeader)
160− ? sessionIdHeader[0]
161− : undefined;
162−
163− if (!sessionId || !transports[sessionId]) {
164− res.status(400).send("Invalid or missing session ID");
165− return;
166− }
167−
168− const transport = transports[sessionId];
169− await transport.handleRequest(req, res, req.body);
170− await transport.close();
171− delete transports[sessionId];
172−
173− if (servers[sessionId]) {
174− await servers[sessionId].close().catch(() => undefined);
175− delete servers[sessionId];
176− }
177− });
178−
179− const listener = await new Promise<import("node:http").Server>((resolve, reject) => {
180− const server = app.listen(input.port ?? 0, host);
181−
182− const onListening = () => {
183− server.off("error", onError);
184− resolve(server);
185− };
186−
187− const onError = (error: Error) => {
188− server.off("listening", onListening);
189− reject(error);
190− };
191−
192− server.once("listening", onListening);
193− server.once("error", onError);
194− });
195−
196− const address = listener.address();
197− if (!address || typeof address === "string") {
198− if (listener.listening) {
199− await new Promise<void>((resolve, reject) => {
200− listener.close((error) => (error ? reject(error) : resolve()));
201− });
202− }
203− throw new Error("Failed to resolve MCP elicitation demo server address");
204− }
205−
206− return {
207− endpoint: `http://${host}:${address.port}/mcp`,
208− close: async () => {
209− for (const transport of Object.values(transports)) {
210− await transport.close().catch(() => undefined);
211− }
212−
213− for (const server of Object.values(servers)) {
214− await server.close().catch(() => undefined);
215− }
216−
217− await new Promise<void>((resolve, reject) => {
218− listener.close((error) => (error ? reject(error) : resolve()));
219− });
220− },
221− };
222−};
apps/mcp-elicitation-demo/tsconfig.json+0−15
@@ -1,15 +0,0 @@
1−{
2− "compilerOptions": {
3− "target": "ES2022",
4− "module": "ESNext",
5− "moduleResolution": "Bundler",
6− "strict": true,
7− "skipLibCheck": true,
8− "noEmit": true,
9− "lib": ["ES2022"],
10− "types": ["bun-types", "node"]
11− },
12− "include": [
13− "src/**/*.ts"
14− ]
15−}