Skip to content

Instantly share code, notes, and snippets.

@kuc-arc-f
Last active November 9, 2025 03:51
Show Gist options
  • Save kuc-arc-f/ab074323c682e29ccbb59503cae5f70e to your computer and use it in GitHub Desktop.
Save kuc-arc-f/ab074323c682e29ccbb59503cae5f70e to your computer and use it in GitHub Desktop.
next.js 16 + Rust MCP Server example
"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>
);
}
// 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);
}
}
// 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;
// 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