Last active
November 9, 2025 03:51
-
-
Save kuc-arc-f/ab074323c682e29ccbb59503cae5f70e to your computer and use it in GitHub Desktop.
next.js 16 + Rust MCP Server example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use client"; | |
| // app/page.tsx | |
| import Image from "next/image"; | |
| export default function Home() { | |
| const testCreate = async function() { | |
| try{ | |
| const res = await fetch("/api/test/create", { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| /* @ts-ignore */ | |
| body: JSON.stringify({name: "tea", price: 140}) | |
| }); | |
| const json = await res.json() | |
| console.log(json) | |
| return json; | |
| }catch(e){console.error(e)} | |
| } | |
| const testList = async function() { | |
| try{ | |
| const res = await fetch("/api/test/list", { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| /* @ts-ignore */ | |
| body: {} | |
| }); | |
| const json = await res.json() | |
| console.log(json) | |
| const out = []; | |
| let rowData = null; | |
| const jsonData = JSON.parse(json.data) | |
| jsonData.forEach((item=>{ | |
| rowData = JSON.parse(item.data) | |
| item.data = rowData; | |
| out.push(item) | |
| })); | |
| console.log(out) | |
| return out; | |
| }catch(e){console.error(e)} | |
| } | |
| return ( | |
| <div className="bg-zinc-50 font-sans dark:bg-black"> | |
| <main className="max-w-3xl flex-col items-center justify-between py-4 px-16 bg-white dark:bg-black sm:items-start"> | |
| <h1 className="text-3xl font-bold my-6">hello</h1> | |
| <hr className="border border-gray-200" /> | |
| <button onClick={()=>testCreate()}>[ Add ]</button> | |
| <hr className="border border-gray-200" /> | |
| <button onClick={()=>testList()}>[ List ]</button> | |
| <hr className="border border-gray-200" /> | |
| </main> | |
| </div> | |
| ); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // app/api/test/create/route.ts | |
| import { NextResponse } from "next/server"; | |
| import RpcClient from '../../../lib/RpcClient' | |
| const __dirname = process.cwd(); | |
| const CMD_PATH = __dirname + "/dist/rust_mcp_server_4" | |
| console.log("CMD_PATH=", CMD_PATH) | |
| export async function POST(req: Request) { | |
| const retObj = {ret: 500, data: null} | |
| try{ | |
| const reqJson = await req.json(); | |
| console.log(reqJson); | |
| const client = new RpcClient(CMD_PATH); | |
| const resp = await client.call( | |
| "tools/call", | |
| { | |
| name: "purchase", | |
| arguments: {name: reqJson.name, price: reqJson.price }, | |
| }, | |
| ); | |
| client.close(); | |
| retObj.ret = 200; | |
| return NextResponse.json(retObj); | |
| } catch (error) { | |
| console.error(error); | |
| return NextResponse.json(retObj); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // app/lib/RpcClient.ts | |
| import { spawn } from "child_process"; | |
| class RpcClient { | |
| constructor(command: any) { | |
| /* @ts-ignore */ | |
| this.proc = spawn(command); | |
| /* @ts-ignore */ | |
| this.idCounter = 1; | |
| /* @ts-ignore */ | |
| this.pending = new Map(); | |
| /* @ts-ignore */ | |
| this.proc.stdout.setEncoding("utf8"); | |
| /* @ts-ignore */ | |
| this.proc.stdout.on("data", (data) => this._handleData(data)); | |
| /* @ts-ignore */ | |
| this.proc.stderr.on("data", (err) => console.error("Rust stderr:", err.toString())); | |
| /* @ts-ignore */ | |
| this.proc.on("exit", (code) => console.log(`Rust server exited (${code})`)); | |
| } | |
| _handleData(data: any) { | |
| // 複数行対応 | |
| /* @ts-ignore */ | |
| data.split("\n").forEach((line) => { | |
| if (!line.trim()) return; | |
| try { | |
| const msg = JSON.parse(line); | |
| /* @ts-ignore */ | |
| if (msg.id && this.pending.has(msg.id)) { | |
| /* @ts-ignore */ | |
| const { resolve } = this.pending.get(msg.id); | |
| /* @ts-ignore */ | |
| this.pending.delete(msg.id); | |
| resolve(msg.result); | |
| } | |
| } catch (e) { | |
| //console.error("JSON parse error:", e, line); | |
| } | |
| }); | |
| } | |
| call(method: any, params = {}) { | |
| /* @ts-ignore */ | |
| const id = this.idCounter++; | |
| const payload = { | |
| jsonrpc: "2.0", | |
| id, | |
| method, | |
| params, | |
| }; | |
| return new Promise((resolve, reject) => { | |
| /* @ts-ignore */ | |
| this.pending.set(id, { resolve, reject }); | |
| /* @ts-ignore */ | |
| this.proc.stdin.write(JSON.stringify(payload) + "\n"); | |
| }); | |
| } | |
| close() { | |
| /* @ts-ignore */ | |
| this.proc.kill(); | |
| } | |
| } | |
| export default RpcClient; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // app/api/test/list/route.ts | |
| import { NextResponse } from "next/server"; | |
| import RpcClient from '../../../lib/RpcClient' | |
| const __dirname = process.cwd(); | |
| const CMD_PATH = __dirname + "/dist/rust_mcp_server_4" | |
| console.log("CMD_PATH=", CMD_PATH) | |
| export async function POST(req: Request) { | |
| const retObj = {ret: 500, data: null} | |
| try{ | |
| //const reqJson = await req.json(); | |
| const client = new RpcClient(CMD_PATH); | |
| const resp = await client.call( | |
| "tools/call", | |
| { | |
| name: "purchase_list", | |
| arguments: null, | |
| }, | |
| ); | |
| client.close(); | |
| console.log("resp=", resp); | |
| /* @ts-ignore */ | |
| if(resp.content[0]){ | |
| retObj.ret = 200; | |
| /* @ts-ignore */ | |
| retObj.data = resp.content[0].text; | |
| return NextResponse.json(retObj); | |
| /* | |
| const json = JSON.parse(resp.content[0].text) | |
| console.log(json) | |
| let rowData = null; | |
| const out = []; | |
| json.forEach((item=>{ | |
| console.log(item.id) | |
| rowData = JSON.parse(item.data) | |
| console.log(rowData) | |
| item.data = rowData; | |
| out.push(item) | |
| })); | |
| console.log(out) | |
| */ | |
| } | |
| return NextResponse.json(retObj); | |
| } catch (error) { | |
| console.error(error); | |
| return NextResponse.json(retObj); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment