Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/simple-chat-client/Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,4 +17,5 @@ rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", features = [
"client",
"transport-child-process",
"transport-sse",
], no-default-features = true }
], no-default-features = true }
clap = { version = "4.0", features = ["derive"] }
10 changes: 5 additions & 5 deletions examples/simple-chat-client/README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,13 +3,13 @@
A simple chat client implementation using the Model Context Protocol (MCP) SDK. It just a example for developers to understand how to use the MCP SDK. This example use the easiest way to start a MCP server, and call the tool directly. No need embedding or complex third library or function call(because some models can't support function call).Just add tool in system prompt, and the client will call the tool automatically.


## Config
the config file is in `src/config.toml`. you can change the config to your own.Move the config file to `/etc/simple-chat-client/config.toml` for system-wide configuration.

## Usage

After configuring the config file, you can run the example:
```bash
cargo run --bin simple_chat
```
./simple_chat --help # show help info
./simple_chat config > config.toml # output default config to file
./simple_chat chat --config my_config.toml # start chat with specified config
./simple_chat chat --config my_config.toml --model gpt-4o-mini # start chat with specified model
```

173 changes: 109 additions & 64 deletions examples/simple-chat-client/src/bin/simple_chat.rs
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,129 @@
use std::sync::Arc;
use std::{process::exit, sync::Arc};

use anyhow::Result;
use clap::{Parser, Subcommand};
use simple_chat_client::{
chat::ChatSession,
client::OpenAIClient,
config::Config,
tool::{Tool, ToolSet, get_mcp_tools},
};

//default config path
const DEFAULT_CONFIG_PATH: &str = "/etc/simple-chat-client/config.toml";
#[derive(Parser)]
#[command(author, version, about = "Simple Chat Client")]
struct Cli {
/// Config file path
#[arg(short, long, value_name = "FILE")]
config: Option<String>,

#[command(subcommand)]
command: Commands,
}

#[derive(Subcommand)]
enum Commands {
/// Output default config template
Config,

/// Start chat
Chat {
/// Specify the model name
#[arg(short, long)]
model: Option<String>,
},
}

#[tokio::main]
async fn main() -> Result<()> {
// load config
let config = Config::load(DEFAULT_CONFIG_PATH).await?;

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("url is {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// create tool set
let mut tool_set = ToolSet::default();

// load mcp
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

for (name, client) in mcp_clients {
println!("loading mcp tools: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("adding tool: {}", tool.name());
tool_set.add_tool(tool);
}
let cli = Cli::parse();

match cli.command {
Commands::Config => {
println!("{}", include_str!("../config.toml"));
return Ok(());
}
}
Commands::Chat { model } => {
// load config
let config_path = cli.config;
let mut config = match config_path {
Some(path) => Config::load(&path).await?,
None => {
println!("No config file provided, using default config");
exit(-1);
}
};

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt with tool info
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters()).unwrap_or_default()
));
}
// if command line specify model, override config file setting
if let Some(model_name) = model {
config.model_name = Some(model_name);
}

// create openai client
let api_key = config
.openai_key
.clone()
.unwrap_or_else(|| std::env::var("OPENAI_API_KEY").expect("need set api key"));
let url = config.chat_url.clone();
println!("use api address: {:?}", url);
let openai_client = Arc::new(OpenAIClient::new(api_key, url, config.proxy));

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);
// create tool set
let mut tool_set = ToolSet::default();

// add system prompt
session.add_system_prompt(system_prompt);
// load MCP
if config.mcp.is_some() {
let mcp_clients = config.create_mcp_clients().await?;

// start chat
session.chat().await?;
for (name, client) in mcp_clients {
println!("load MCP tool: {}", name);
let server = client.peer().clone();
let tools = get_mcp_tools(server).await?;

for tool in tools {
println!("add tool: {}", tool.name());
tool_set.add_tool(tool);
}
}
}

// create chat session
let mut session = ChatSession::new(
openai_client,
tool_set,
config
.model_name
.unwrap_or_else(|| "gpt-4o-mini".to_string()),
);

// build system prompt
let mut system_prompt =
"you are a assistant, you can help user to complete various tasks. you have the following tools to use:\n".to_string();

// add tool info to system prompt
for tool in session.get_tools() {
system_prompt.push_str(&format!(
"\ntool name: {}\ndescription: {}\nparameters: {}\n",
tool.name(),
tool.description(),
serde_json::to_string_pretty(&tool.parameters())
.expect("failed to serialize tool parameters")
));
}

// add tool call format guidance
system_prompt.push_str(
"\nif you need to call tool, please use the following format:\n\
Tool: <tool name>\n\
Inputs: <inputs>\n",
);

// add system prompt
session.add_system_prompt(system_prompt);

// start chat
session.chat().await?;
}
}

Ok(())
}