Created
October 16, 2025 01:12
-
-
Save kuc-arc-f/0ea1a8c22ae60f6d8c1a005e84cab059 to your computer and use it in GitHub Desktop.
Rust , remoto 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
[package] | |
name = "rust_remoto_mcp_1" | |
version = "0.1.0" | |
edition = "2024" | |
[dependencies] | |
anyhow = "1.0.100" | |
axum = "0.7.5" | |
axum-extra = { version = "0.10.1", features = ["cookie"] } | |
chrono = { version = "0.4", features = ["serde"] } | |
serde = { version = "1.0", features = ["derive"] } | |
serde_json = "1.0" | |
tokio = { version = "1.0", features = ["full"] } | |
tower = "0.4" | |
tower-http = { version = "0.6.6", features = ["fs", "trace"] } | |
tracing = "0.1" | |
tracing-subscriber = { version = "0.3", features = ["env-filter"] } |
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 axum::{ | |
extract::State, | |
http::StatusCode, | |
response::IntoResponse, | |
routing::post, | |
Json, Router, | |
}; | |
use serde::{Deserialize, Serialize}; | |
use serde_json::{json, Value}; | |
use std::sync::Arc; | |
use tower_http::trace::TraceLayer; | |
// JSON-RPC 2.0 Request | |
#[derive(Debug, Deserialize)] | |
struct JsonRpcRequest { | |
jsonrpc: String, | |
method: String, | |
#[serde(default)] | |
params: Option<Value>, | |
id: Option<Value>, | |
} | |
// JSON-RPC 2.0 Response | |
#[derive(Debug, Serialize)] | |
struct JsonRpcResponse { | |
jsonrpc: String, | |
#[serde(skip_serializing_if = "Option::is_none")] | |
result: Option<Value>, | |
#[serde(skip_serializing_if = "Option::is_none")] | |
error: Option<JsonRpcError>, | |
id: Option<Value>, | |
} | |
// JSON-RPC 2.0 Error | |
#[derive(Debug, Serialize)] | |
struct JsonRpcError { | |
code: i32, | |
message: String, | |
#[serde(skip_serializing_if = "Option::is_none")] | |
data: Option<Value>, | |
} | |
// MCP Server State | |
#[derive(Clone)] | |
struct AppState { | |
server_name: String, | |
version: String, | |
} | |
#[tokio::main] | |
async fn main() { | |
// ロギング初期化 | |
tracing_subscriber::fmt() | |
.with_target(false) | |
.compact() | |
.init(); | |
// アプリケーションステート | |
let state = Arc::new(AppState { | |
server_name: "MCP Server Example".to_string(), | |
version: "1.0.0".to_string(), | |
}); | |
// ルーター設定 | |
let app = Router::new() | |
.route("/", post(handle_jsonrpc)) | |
.route("/mcp", post(handle_jsonrpc)) | |
.with_state(state) | |
.layer(TraceLayer::new_for_http()); | |
// サーバー起動 | |
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") | |
.await | |
.unwrap(); | |
tracing::info!("MCP Server listening on {}", listener.local_addr().unwrap()); | |
axum::serve(listener, app).await.unwrap(); | |
} | |
// JSON-RPC ハンドラー | |
async fn handle_jsonrpc( | |
State(state): State<Arc<AppState>>, | |
Json(request): Json<JsonRpcRequest>, | |
) -> impl IntoResponse { | |
tracing::info!("Received request: method={}, id={:?}", request.method, request.id); | |
// JSON-RPC 2.0 バージョンチェック | |
if request.jsonrpc != "2.0" { | |
return ( | |
StatusCode::OK, | |
Json(JsonRpcResponse { | |
jsonrpc: "2.0".to_string(), | |
result: None, | |
error: Some(JsonRpcError { | |
code: -32600, | |
message: "Invalid Request".to_string(), | |
data: None, | |
}), | |
id: request.id, | |
}), | |
); | |
} | |
// メソッドディスパッチ | |
let result = match request.method.as_str() { | |
"initialize" => handle_initialize(&state, request.params), | |
"tools/list" => handle_tools_list(), | |
"tools/call" => handle_tools_call(request.params), | |
"resources/list" => handle_resources_list(), | |
"resources/read" => handle_resources_read(request.params), | |
"prompts/list" => handle_prompts_list(), | |
_ => Err(JsonRpcError { | |
code: -32601, | |
message: "Method not found".to_string(), | |
data: None, | |
}), | |
}; | |
let response = match result { | |
Ok(result) => JsonRpcResponse { | |
jsonrpc: "2.0".to_string(), | |
result: Some(result), | |
error: None, | |
id: request.id, | |
}, | |
Err(error) => JsonRpcResponse { | |
jsonrpc: "2.0".to_string(), | |
result: None, | |
error: Some(error), | |
id: request.id, | |
}, | |
}; | |
(StatusCode::OK, Json(response)) | |
} | |
// MCP initialize メソッド | |
fn handle_initialize(state: &AppState, _params: Option<Value>) -> Result<Value, JsonRpcError> { | |
Ok(json!({ | |
"protocolVersion": "2024-11-05", | |
"serverInfo": { | |
"name": state.server_name, | |
"version": state.version | |
}, | |
"capabilities": { | |
"tools": {}, | |
"resources": {}, | |
"prompts": {} | |
} | |
})) | |
} | |
// tools/list メソッド | |
fn handle_tools_list() -> Result<Value, JsonRpcError> { | |
Ok(json!({ | |
"tools": [ | |
{ | |
"name": "echo", | |
"description": "Echo back the input message", | |
"inputSchema": { | |
"type": "object", | |
"properties": { | |
"message": { | |
"type": "string", | |
"description": "Message to echo" | |
} | |
}, | |
"required": ["message"] | |
} | |
}, | |
{ | |
"name": "add", | |
"description": "Add two numbers", | |
"inputSchema": { | |
"type": "object", | |
"properties": { | |
"a": { "type": "number" }, | |
"b": { "type": "number" } | |
}, | |
"required": ["a", "b"] | |
} | |
} | |
] | |
})) | |
} | |
// tools/call メソッド | |
fn handle_tools_call(params: Option<Value>) -> Result<Value, JsonRpcError> { | |
let params = params.ok_or(JsonRpcError { | |
code: -32602, | |
message: "Invalid params".to_string(), | |
data: None, | |
})?; | |
let tool_name = params["name"].as_str().ok_or(JsonRpcError { | |
code: -32602, | |
message: "Tool name is required".to_string(), | |
data: None, | |
})?; | |
let arguments = ¶ms["arguments"]; | |
match tool_name { | |
"echo" => { | |
let message = arguments["message"].as_str().unwrap_or("No message"); | |
Ok(json!({ | |
"content": [ | |
{ | |
"type": "text", | |
"text": format!("Echo: {}", message) | |
} | |
] | |
})) | |
} | |
"add" => { | |
let a = arguments["a"].as_f64().unwrap_or(0.0); | |
let b = arguments["b"].as_f64().unwrap_or(0.0); | |
Ok(json!({ | |
"content": [ | |
{ | |
"type": "text", | |
"text": format!("Result: {}", a + b) | |
} | |
] | |
})) | |
} | |
_ => Err(JsonRpcError { | |
code: -32602, | |
message: format!("Unknown tool: {}", tool_name), | |
data: None, | |
}), | |
} | |
} | |
// resources/list メソッド | |
fn handle_resources_list() -> Result<Value, JsonRpcError> { | |
Ok(json!({ | |
"resources": [ | |
{ | |
"uri": "file:///example.txt", | |
"name": "Example Resource", | |
"description": "An example resource", | |
"mimeType": "text/plain" | |
} | |
] | |
})) | |
} | |
// resources/read メソッド | |
fn handle_resources_read(params: Option<Value>) -> Result<Value, JsonRpcError> { | |
let params = params.ok_or(JsonRpcError { | |
code: -32602, | |
message: "Invalid params".to_string(), | |
data: None, | |
})?; | |
let uri = params["uri"].as_str().ok_or(JsonRpcError { | |
code: -32602, | |
message: "URI is required".to_string(), | |
data: None, | |
})?; | |
Ok(json!({ | |
"contents": [ | |
{ | |
"uri": uri, | |
"mimeType": "text/plain", | |
"text": "This is the content of the resource" | |
} | |
] | |
})) | |
} | |
// prompts/list メソッド | |
fn handle_prompts_list() -> Result<Value, JsonRpcError> { | |
Ok(json!({ | |
"prompts": [ | |
{ | |
"name": "example_prompt", | |
"description": "An example prompt", | |
"arguments": [] | |
} | |
] | |
})) | |
} |
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
const start = async function() { | |
try{ | |
const item = { | |
"jsonrpc": "2.0", | |
"method": "tools/list", | |
"id": 2 | |
} | |
const response = await fetch("http://localhost:3000/mcp", { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'Authorization': '', | |
}, | |
body: JSON.stringify(item), | |
}); | |
if (!response.ok) { | |
const text = await response.text(); | |
console.log(text); | |
throw new Error('Failed to create item'); | |
}else{ | |
const json = await response.json(); | |
console.log(json); | |
console.log(json.result.tools); | |
} | |
}catch(e){console.log(e)} | |
} | |
start(); |
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
const start = async function() { | |
try{ | |
const item = { | |
"jsonrpc": "2.0", | |
"method": "tools/call", | |
"params": { | |
"name": "echo", | |
"arguments": { | |
"message": "Hello, MCP!" | |
} | |
}, | |
"id": 2 | |
} | |
const response = await fetch("http://localhost:3000/mcp", { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'Authorization': '', | |
}, | |
body: JSON.stringify(item), | |
}); | |
if (!response.ok) { | |
const text = await response.text(); | |
console.log(text); | |
throw new Error('Failed to create item'); | |
}else{ | |
const json = await response.json(); | |
console.log(json); | |
console.log("text=", json.result.content[0].text); | |
} | |
}catch(e){console.log(e)} | |
} | |
start(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment