mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-25 09:48:10 -07:00
mcp: part 2
This commit is contained in:
Generated
+60
-44
@@ -585,11 +585,11 @@ dependencies = [
|
|||||||
"brk_computer",
|
"brk_computer",
|
||||||
"brk_core",
|
"brk_core",
|
||||||
"brk_indexer",
|
"brk_indexer",
|
||||||
|
"brk_rmcp",
|
||||||
"brk_vec",
|
"brk_vec",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"derive_deref",
|
"derive_deref",
|
||||||
"rmcp",
|
"schemars 1.0.0-rc.2",
|
||||||
"schemars",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
@@ -621,6 +621,48 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brk_rmcp"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90d257949059da288c47b4f88ac80abcaa18afe65972d8b543369ee2099b37db"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"brk_rmcp-macros",
|
||||||
|
"bytes",
|
||||||
|
"chrono",
|
||||||
|
"futures",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"paste",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rand 0.9.1",
|
||||||
|
"schemars 1.0.0-rc.2",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sse-stream",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tokio-util",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brk_rmcp-macros"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12869ae1128aad2e0777db2688e661a55d389ecfb863936145ea1b789e0c2d34"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"serde_json",
|
||||||
|
"syn 2.0.103",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "brk_rolldown"
|
name = "brk_rolldown"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
@@ -941,6 +983,7 @@ dependencies = [
|
|||||||
"brk_interface",
|
"brk_interface",
|
||||||
"brk_logger",
|
"brk_logger",
|
||||||
"brk_parser",
|
"brk_parser",
|
||||||
|
"brk_rmcp",
|
||||||
"brk_vec",
|
"brk_vec",
|
||||||
"clap",
|
"clap",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -948,7 +991,6 @@ dependencies = [
|
|||||||
"jiff",
|
"jiff",
|
||||||
"log",
|
"log",
|
||||||
"minreq",
|
"minreq",
|
||||||
"rmcp",
|
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -3402,44 +3444,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rmcp"
|
|
||||||
version = "0.1.6"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.22.1",
|
|
||||||
"bytes",
|
|
||||||
"chrono",
|
|
||||||
"futures",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"http-body-util",
|
|
||||||
"paste",
|
|
||||||
"pin-project-lite",
|
|
||||||
"rand 0.9.1",
|
|
||||||
"rmcp-macros",
|
|
||||||
"schemars",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sse-stream",
|
|
||||||
"thiserror 2.0.12",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tokio-util",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rmcp-macros"
|
|
||||||
version = "0.1.6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"serde_json",
|
|
||||||
"syn 2.0.103",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rolldown-ariadne"
|
name = "rolldown-ariadne"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -3539,6 +3543,18 @@ name = "schemars"
|
|||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
|
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
|
||||||
|
dependencies = [
|
||||||
|
"dyn-clone",
|
||||||
|
"ref-cast",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schemars"
|
||||||
|
version = "1.0.0-rc.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "284cf8058b5165c2e1aedb8f56f4b09a838a764d1c110178e1747d1f77a01ceb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
@@ -3549,9 +3565,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars_derive"
|
name = "schemars_derive"
|
||||||
version = "0.9.0"
|
version = "1.0.0-rc.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5016d94c77c6d32f0b8e08b781f7dc8a90c2007d4e77472cc2807bc10a8438fe"
|
checksum = "3ce51e6bea03670e88edba2d724eed817b97f4ab017a1001bae5da325ce93fe7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3713,7 +3729,7 @@ dependencies = [
|
|||||||
"hex",
|
"hex",
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"indexmap 2.9.0",
|
"indexmap 2.9.0",
|
||||||
"schemars",
|
"schemars 0.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
+3
-5
@@ -29,9 +29,10 @@ brk_core = { version = "0.0.66", path = "crates/brk_core" }
|
|||||||
brk_exit = { version = "0.0.66", path = "crates/brk_exit" }
|
brk_exit = { version = "0.0.66", path = "crates/brk_exit" }
|
||||||
brk_fetcher = { version = "0.0.66", path = "crates/brk_fetcher" }
|
brk_fetcher = { version = "0.0.66", path = "crates/brk_fetcher" }
|
||||||
brk_indexer = { version = "0.0.66", path = "crates/brk_indexer" }
|
brk_indexer = { version = "0.0.66", path = "crates/brk_indexer" }
|
||||||
|
brk_interface = { version = "0.0.66", path = "crates/brk_interface" }
|
||||||
brk_logger = { version = "0.0.66", path = "crates/brk_logger" }
|
brk_logger = { version = "0.0.66", path = "crates/brk_logger" }
|
||||||
brk_parser = { version = "0.0.66", path = "crates/brk_parser" }
|
brk_parser = { version = "0.0.66", path = "crates/brk_parser" }
|
||||||
brk_interface = { version = "0.0.66", path = "crates/brk_interface" }
|
brk_rmcp = { version = "0.1.6", features = ["transport-streamable-http-server", "transport-worker"]}
|
||||||
brk_server = { version = "0.0.66", path = "crates/brk_server" }
|
brk_server = { version = "0.0.66", path = "crates/brk_server" }
|
||||||
brk_state = { version = "0.0.66", path = "crates/brk_state" }
|
brk_state = { version = "0.0.66", path = "crates/brk_state" }
|
||||||
brk_store = { version = "0.0.66", path = "crates/brk_store" }
|
brk_store = { version = "0.0.66", path = "crates/brk_store" }
|
||||||
@@ -46,10 +47,7 @@ jiff = "0.2.15"
|
|||||||
log = { version = "0.4.27" }
|
log = { version = "0.4.27" }
|
||||||
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
|
minreq = { version = "2.13.4", features = ["https", "serde_json"] }
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
rmcp = { path = "../rust-sdk/crates/rmcp", features = ["transport-streamable-http-server", "transport-worker"] }
|
schemars = "1.0.0-rc.2"
|
||||||
schemars = "0.9.0"
|
|
||||||
# rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "main" , features = ["transport-streamable-http-server", "transport-worker"] }
|
|
||||||
# schemars = "0.8.0"
|
|
||||||
serde = { version = "1.0.219" }
|
serde = { version = "1.0.219" }
|
||||||
serde_bytes = "0.11.17"
|
serde_bytes = "0.11.17"
|
||||||
serde_derive = "1.0.219"
|
serde_derive = "1.0.219"
|
||||||
|
|||||||
@@ -87,7 +87,12 @@ pub struct Config {
|
|||||||
#[arg(long, value_name = "SECONDS")]
|
#[arg(long, value_name = "SECONDS")]
|
||||||
delay: Option<u64>,
|
delay: Option<u64>,
|
||||||
|
|
||||||
/// DEV: Activate to watch the selected website's folder for changes, default: false, saved
|
/// Activate the Model Context Protocol (MCP) endpoint to give LLMs access to BRK, default: false, saved
|
||||||
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
|
#[arg(long, value_name = "BOOL")]
|
||||||
|
mcp: Option<bool>,
|
||||||
|
|
||||||
|
/// DEV: Activate watching the selected website's folder for changes, default: false, saved
|
||||||
#[serde(default, deserialize_with = "default_on_error")]
|
#[serde(default, deserialize_with = "default_on_error")]
|
||||||
#[arg(long, value_name = "BOOL")]
|
#[arg(long, value_name = "BOOL")]
|
||||||
watch: Option<bool>,
|
watch: Option<bool>,
|
||||||
@@ -171,6 +176,10 @@ impl Config {
|
|||||||
config_saved.check_collisions = Some(check_collisions);
|
config_saved.check_collisions = Some(check_collisions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(mcp) = config_args.mcp.take() {
|
||||||
|
config_saved.mcp = Some(mcp);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(watch) = config_args.watch.take() {
|
if let Some(watch) = config_args.watch.take() {
|
||||||
config_saved.watch = Some(watch);
|
config_saved.watch = Some(watch);
|
||||||
}
|
}
|
||||||
@@ -356,6 +365,10 @@ impl Config {
|
|||||||
self.check_collisions.is_some_and(|b| b)
|
self.check_collisions.is_some_and(|b| b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mcp(&self) -> bool {
|
||||||
|
self.mcp.is_some_and(|b| b)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn watch(&self) -> bool {
|
pub fn watch(&self) -> bool {
|
||||||
self.watch.is_some_and(|b| b)
|
self.watch.is_some_and(|b| b)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ pub fn run() -> color_eyre::Result<()> {
|
|||||||
let server = Server::new(served_indexer, served_computer, config.website())?;
|
let server = Server::new(served_indexer, served_computer, config.website())?;
|
||||||
|
|
||||||
let watch = config.watch();
|
let watch = config.watch();
|
||||||
|
let mcp = config.mcp();
|
||||||
let opt = Some(tokio::spawn(async move {
|
let opt = Some(tokio::spawn(async move {
|
||||||
server.serve(watch).await.unwrap();
|
server.serve(watch, mcp).await.unwrap();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
sleep(Duration::from_secs(1));
|
sleep(Duration::from_secs(1));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ brk_indexer = { workspace = true }
|
|||||||
brk_vec = { workspace = true }
|
brk_vec = { workspace = true }
|
||||||
color-eyre = { workspace = true }
|
color-eyre = { workspace = true }
|
||||||
derive_deref = { workspace = true }
|
derive_deref = { workspace = true }
|
||||||
rmcp = { workspace = true }
|
brk_rmcp = { workspace = true }
|
||||||
schemars = { workspace = true }
|
schemars = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
use brk_rmcp::schemars::JsonSchema;
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use rmcp::schemars::JsonSchema;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ impl<'de> Deserialize<'de> for Index {
|
|||||||
{
|
{
|
||||||
let str = String::deserialize(deserializer)?;
|
let str = String::deserialize(deserializer)?;
|
||||||
if let Ok(index) = Index::try_from(str.as_str()) {
|
if let Ok(index) = Index::try_from(str.as_str()) {
|
||||||
|
// dbg!(index);
|
||||||
Ok(index)
|
Ok(index)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::custom("Bad index"))
|
Err(Error::custom("Bad index"))
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ mod format;
|
|||||||
mod index;
|
mod index;
|
||||||
mod maybe_ids;
|
mod maybe_ids;
|
||||||
mod output;
|
mod output;
|
||||||
|
mod pagination;
|
||||||
mod params;
|
mod params;
|
||||||
mod table;
|
mod table;
|
||||||
mod vecs;
|
mod vecs;
|
||||||
@@ -22,7 +23,8 @@ mod vecs;
|
|||||||
pub use format::Format;
|
pub use format::Format;
|
||||||
pub use index::Index;
|
pub use index::Index;
|
||||||
pub use output::{Output, Value};
|
pub use output::{Output, Value};
|
||||||
pub use params::{Pagination, Params, ParamsOpt};
|
pub use pagination::{PaginatedIndexParam, PaginationParam};
|
||||||
|
pub use params::{IdParam, Params, ParamsOpt};
|
||||||
pub use table::Tabled;
|
pub use table::Tabled;
|
||||||
use vecs::Vecs;
|
use vecs::Vecs;
|
||||||
|
|
||||||
@@ -192,21 +194,15 @@ impl<'a> Interface<'a> {
|
|||||||
&self.vecs.accepted_indexes
|
&self.vecs.accepted_indexes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vecids(&self, pagination: Pagination) -> &[&str] {
|
pub fn get_vecids(&self, pagination: PaginationParam) -> &[&str] {
|
||||||
self.vecs.ids(pagination)
|
self.vecs.ids(pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_indexes_to_vecids(
|
pub fn get_index_to_vecids(&self, paginated_index: PaginatedIndexParam) -> Vec<&str> {
|
||||||
&self,
|
self.vecs.index_to_ids(paginated_index)
|
||||||
pagination: Pagination,
|
|
||||||
) -> BTreeMap<&'static str, Vec<&str>> {
|
|
||||||
self.vecs.indexes_to_ids(pagination)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_vecids_to_indexes(
|
pub fn get_vecid_to_indexes(&self, id: String) -> Option<&Vec<&'static str>> {
|
||||||
&self,
|
self.vecs.id_to_indexes(id)
|
||||||
pagination: Pagination,
|
|
||||||
) -> BTreeMap<&str, Vec<&'static str>> {
|
|
||||||
self.vecs.ids_to_indexes(pagination)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,17 @@ impl<'de> Deserialize<'de> for MaybeIds {
|
|||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let str = String::deserialize(deserializer)?;
|
let maybe_ids = match serde_json::Value::deserialize(deserializer)? {
|
||||||
Ok(MaybeIds(
|
serde_json::Value::String(str) => {
|
||||||
str.split(",").map(|s| s.to_string()).collect::<Vec<_>>(),
|
str.split(",").map(|s| s.to_string()).collect::<Vec<_>>()
|
||||||
))
|
}
|
||||||
|
serde_json::Value::Array(vec) => vec
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.as_str().unwrap().to_string())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
_ => return Err(serde::de::Error::custom("Bad ids format")),
|
||||||
|
};
|
||||||
|
// dbg!(&maybe_ids);
|
||||||
|
Ok(MaybeIds(maybe_ids))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use tabled::Tabled as TabledTabled;
|
|||||||
use crate::Format;
|
use crate::Format;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
pub enum Output {
|
pub enum Output {
|
||||||
Json(Value),
|
Json(Value),
|
||||||
CSV(String),
|
CSV(String),
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::Index;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, JsonSchema)]
|
||||||
|
pub struct PaginationParam {
|
||||||
|
#[serde(alias = "p")]
|
||||||
|
#[schemars(description = "Pagination index")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub page: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaginationParam {
|
||||||
|
const PER_PAGE: usize = 1_000;
|
||||||
|
|
||||||
|
pub fn start(&self, len: usize) -> usize {
|
||||||
|
(self.page * Self::PER_PAGE).clamp(0, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&self, len: usize) -> usize {
|
||||||
|
((self.page + 1) * Self::PER_PAGE).clamp(0, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct PaginatedIndexParam {
|
||||||
|
pub index: Index,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub pagination: PaginationParam,
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use rmcp::schemars::{self, JsonSchema};
|
use brk_rmcp::schemars::{self, JsonSchema};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
use crate::{Format, Index, maybe_ids::MaybeIds};
|
use crate::{Format, Index, maybe_ids::MaybeIds};
|
||||||
@@ -52,6 +52,7 @@ pub struct ParamsOpt {
|
|||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
|
||||||
/// Format of the output
|
/// Format of the output
|
||||||
|
#[serde(default)]
|
||||||
format: Option<Format>,
|
format: Option<Format>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ impl ParamsOpt {
|
|||||||
if let Some(c) = self.count {
|
if let Some(c) = self.count {
|
||||||
let c = c as i64;
|
let c = c as i64;
|
||||||
if let Some(f) = self.from {
|
if let Some(f) = self.from {
|
||||||
if f.is_positive() || f.abs() > c {
|
if f >= 0 || f.abs() > c {
|
||||||
return Some(f + c);
|
return Some(f + c);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -101,6 +102,11 @@ impl ParamsOpt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, JsonSchema)]
|
||||||
|
pub struct IdParam {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
fn de_unquote_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
|
fn de_unquote_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
@@ -154,22 +160,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, JsonSchema)]
|
|
||||||
pub struct Pagination {
|
|
||||||
#[serde(alias = "p")]
|
|
||||||
#[schemars(description = "Pagination index")]
|
|
||||||
pub page: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pagination {
|
|
||||||
const PER_PAGE: usize = 1_000;
|
|
||||||
|
|
||||||
pub fn start(&self, len: usize) -> usize {
|
|
||||||
(self.page * Self::PER_PAGE).clamp(0, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end(&self, len: usize) -> usize {
|
|
||||||
((self.page + 1) * Self::PER_PAGE).clamp(0, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use brk_indexer::Indexer;
|
|||||||
use brk_vec::AnyCollectableVec;
|
use brk_vec::AnyCollectableVec;
|
||||||
use derive_deref::{Deref, DerefMut};
|
use derive_deref::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::params::Pagination;
|
use crate::pagination::{PaginatedIndexParam, PaginationParam};
|
||||||
|
|
||||||
use super::index::Index;
|
use super::index::Index;
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ pub struct Vecs<'a> {
|
|||||||
pub index_count: usize,
|
pub index_count: usize,
|
||||||
pub id_count: usize,
|
pub id_count: usize,
|
||||||
pub vec_count: usize,
|
pub vec_count: usize,
|
||||||
serialized_id_to_indexes: BTreeMap<&'a str, Vec<&'static str>>,
|
id_to_indexes: BTreeMap<&'a str, Vec<&'static str>>,
|
||||||
serialized_indexes_to_ids: BTreeMap<&'static str, Vec<&'a str>>,
|
indexes_to_ids: BTreeMap<Index, Vec<&'a str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Vecs<'a> {
|
impl<'a> Vecs<'a> {
|
||||||
@@ -40,17 +40,22 @@ impl<'a> Vecs<'a> {
|
|||||||
.for_each(|vec| this.insert(vec));
|
.for_each(|vec| this.insert(vec));
|
||||||
|
|
||||||
let mut ids = this.id_to_index_to_vec.keys().cloned().collect::<Vec<_>>();
|
let mut ids = this.id_to_index_to_vec.keys().cloned().collect::<Vec<_>>();
|
||||||
ids.sort_unstable_by(|a, b| {
|
|
||||||
let len_cmp = a.len().cmp(&b.len());
|
let sort_ids = |ids: &mut Vec<&str>| {
|
||||||
if len_cmp == std::cmp::Ordering::Equal {
|
ids.sort_unstable_by(|a, b| {
|
||||||
a.cmp(b)
|
let len_cmp = a.len().cmp(&b.len());
|
||||||
} else {
|
if len_cmp == std::cmp::Ordering::Equal {
|
||||||
len_cmp
|
a.cmp(b)
|
||||||
}
|
} else {
|
||||||
});
|
len_cmp
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
sort_ids(&mut ids);
|
||||||
|
|
||||||
this.ids = ids;
|
this.ids = ids;
|
||||||
this.id_count = this.index_to_id_to_vec.keys().count();
|
this.id_count = this.id_to_index_to_vec.keys().count();
|
||||||
this.index_count = this.index_to_id_to_vec.keys().count();
|
this.index_count = this.index_to_id_to_vec.keys().count();
|
||||||
this.vec_count = this
|
this.vec_count = this
|
||||||
.index_to_id_to_vec
|
.index_to_id_to_vec
|
||||||
@@ -67,7 +72,7 @@ impl<'a> Vecs<'a> {
|
|||||||
.keys()
|
.keys()
|
||||||
.map(|i| (i.serialize_long(), i.possible_values()))
|
.map(|i| (i.serialize_long(), i.possible_values()))
|
||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
this.serialized_id_to_indexes = this
|
this.id_to_indexes = this
|
||||||
.id_to_index_to_vec
|
.id_to_index_to_vec
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(id, index_to_vec)| {
|
.map(|(id, index_to_vec)| {
|
||||||
@@ -80,16 +85,14 @@ impl<'a> Vecs<'a> {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
this.serialized_indexes_to_ids = this
|
this.indexes_to_ids = this
|
||||||
.index_to_id_to_vec
|
.index_to_id_to_vec
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(index, id_to_vec)| {
|
.map(|(index, id_to_vec)| (*index, id_to_vec.keys().cloned().collect::<Vec<_>>()))
|
||||||
(
|
|
||||||
index.serialize_long(),
|
|
||||||
id_to_vec.keys().cloned().collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
this.indexes_to_ids
|
||||||
|
.values_mut()
|
||||||
|
.for_each(|ids| sort_ids(ids));
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@@ -150,35 +153,28 @@ impl<'a> Vecs<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ids(&self, pagination: Pagination) -> &[&'_ str] {
|
pub fn ids(&self, pagination: PaginationParam) -> &[&'_ str] {
|
||||||
let len = self.ids.len();
|
let len = self.ids.len();
|
||||||
let start = pagination.start(len);
|
let start = pagination.start(len);
|
||||||
let end = pagination.end(len);
|
let end = pagination.end(len);
|
||||||
&self.ids[start..end]
|
&self.ids[start..end]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ids_to_indexes(&self, pagination: Pagination) -> BTreeMap<&'_ str, Vec<&'static str>> {
|
pub fn id_to_indexes(&self, id: String) -> Option<&Vec<&'static str>> {
|
||||||
let len = self.serialized_id_to_indexes.len();
|
self.id_to_indexes.get(id.as_str())
|
||||||
let start = pagination.start(len);
|
|
||||||
let end = pagination.end(len);
|
|
||||||
self.serialized_id_to_indexes
|
|
||||||
.iter()
|
|
||||||
.skip(start)
|
|
||||||
.take(end)
|
|
||||||
.map(|(ids, indexes)| (*ids, indexes.clone()))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indexes_to_ids(&self, pagination: Pagination) -> BTreeMap<&'static str, Vec<&'a str>> {
|
pub fn index_to_ids(
|
||||||
let len = self.serialized_indexes_to_ids.len();
|
&self,
|
||||||
|
PaginatedIndexParam { index, pagination }: PaginatedIndexParam,
|
||||||
|
) -> Vec<&'a str> {
|
||||||
|
let vec = self.indexes_to_ids.get(&index).unwrap();
|
||||||
|
|
||||||
|
let len = vec.len();
|
||||||
let start = pagination.start(len);
|
let start = pagination.start(len);
|
||||||
let end = pagination.end(len);
|
let end = pagination.end(len);
|
||||||
self.serialized_indexes_to_ids
|
|
||||||
.iter()
|
vec.iter().skip(start).take(end).cloned().collect()
|
||||||
.skip(start)
|
|
||||||
.take(end)
|
|
||||||
.map(|(index, ids)| (*index, ids.clone()))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,9 @@ pub fn init(path: Option<&Path>) {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
Builder::from_env(
|
Builder::from_env(Env::default().default_filter_or(
|
||||||
Env::default()
|
"info,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off,rmcp=off,brk_rmcp=off,tracing=off",
|
||||||
.default_filter_or("info,fjall=off,lsm_tree=off,rolldown=off,brk_rolldown=off"),
|
))
|
||||||
)
|
|
||||||
.format(move |buf, record| {
|
.format(move |buf, record| {
|
||||||
let date_time = Timestamp::now()
|
let date_time = Timestamp::now()
|
||||||
.to_zoned(tz::TimeZone::system())
|
.to_zoned(tz::TimeZone::system())
|
||||||
@@ -83,6 +82,7 @@ fn write(
|
|||||||
args: impl Display,
|
args: impl Display,
|
||||||
) -> Result<(), std::io::Error> {
|
) -> Result<(), std::io::Error> {
|
||||||
writeln!(buf, "{} {} {} {}", date_time, dash, level, args)
|
writeln!(buf, "{} {} {} {}", date_time, dash, level, args)
|
||||||
|
// Don't remove, used to know the target of unwanted logs
|
||||||
// writeln!(
|
// writeln!(
|
||||||
// buf,
|
// buf,
|
||||||
// "{} {} {} {} {}",
|
// "{} {} {} {} {}",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ color-eyre = { workspace = true }
|
|||||||
jiff = { workspace = true }
|
jiff = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
minreq = { workspace = true }
|
minreq = { workspace = true }
|
||||||
rmcp = { workspace = true }
|
brk_rmcp = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
tower-http = { version = "0.6.6", features = ["compression-full", "trace"] }
|
||||||
|
|||||||
@@ -43,27 +43,30 @@ The API uses `brk_interface` and so inherites all of its features including form
|
|||||||
|
|
||||||
#### [`GET /api/vecs/index-count`](https://bitcoinresearchkit.org/api/vecs/index-count)
|
#### [`GET /api/vecs/index-count`](https://bitcoinresearchkit.org/api/vecs/index-count)
|
||||||
|
|
||||||
Count of all possible indexes
|
Get the count of all existing indexes.
|
||||||
|
|
||||||
#### [`GET /api/vecs/id-count`](https://bitcoinresearchkit.org/api/vecs/id-count)
|
#### [`GET /api/vecs/id-count`](https://bitcoinresearchkit.org/api/vecs/id-count)
|
||||||
|
|
||||||
Count of all possible ids
|
Get the count of all existing vec ids.
|
||||||
|
|
||||||
#### [`GET /api/vecs/variant-count`](https://bitcoinresearchkit.org/api/vecs/variant-count)
|
#### [`GET /api/vecs/variant-count`](https://bitcoinresearchkit.org/api/vecs/variant-count)
|
||||||
|
|
||||||
Count of all possible variants
|
Get the count of all existing vecs. \
|
||||||
|
Equals to the sum of supported Indexes of each vec id.
|
||||||
|
|
||||||
#### [`GET /api/vecs/indexes`](https://bitcoinresearchkit.org/api/vecs/indexes)
|
#### [`GET /api/vecs/indexes`](https://bitcoinresearchkit.org/api/vecs/indexes)
|
||||||
|
|
||||||
Get a list of all possible indexes
|
Get the list of all existing indexes.
|
||||||
|
|
||||||
#### [`GET /api/vecs/accepted-indexes`](https://bitcoinresearchkit.org/api/vecs/accepted-indexes)
|
#### [`GET /api/vecs/accepted-indexes`](https://bitcoinresearchkit.org/api/vecs/accepted-indexes)
|
||||||
|
|
||||||
Get a list of possible indexes and all their accepted variants
|
Get an object which has all existing indexes as keys and a list of their accepted variants as values.
|
||||||
|
|
||||||
#### [`GET /api/vecs/ids`](https://bitcoinresearchkit.org/api/vecs/ids)
|
#### [`GET /api/vecs/ids`](https://bitcoinresearchkit.org/api/vecs/ids)
|
||||||
|
|
||||||
A list of all possible vec ids
|
Get a paginated list of all existing vec ids. \
|
||||||
|
There are up to 1,000 values per page. \
|
||||||
|
If the `page` param is omitted, it will default to page `0`.
|
||||||
|
|
||||||
#### [`GET /api/vecs/variants`](https://bitcoinresearchkit.org/api/vecs/variants)
|
#### [`GET /api/vecs/variants`](https://bitcoinresearchkit.org/api/vecs/variants)
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ pub fn main() -> color_eyre::Result<()> {
|
|||||||
let server = Server::new(served_indexer, served_computer, Website::Default)?;
|
let server = Server::new(served_indexer, served_computer, Website::Default)?;
|
||||||
|
|
||||||
let server = tokio::spawn(async move {
|
let server = tokio::spawn(async move {
|
||||||
server.serve(true).await.unwrap();
|
server.serve(true, true).await.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
if process {
|
if process {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use axum::{
|
|||||||
response::{IntoResponse, Redirect, Response},
|
response::{IntoResponse, Redirect, Response},
|
||||||
routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
use brk_interface::{Index, Pagination, Params, ParamsOpt};
|
use brk_interface::{IdParam, Index, PaginatedIndexParam, PaginationParam, Params, ParamsOpt};
|
||||||
|
|
||||||
use super::AppState;
|
use super::AppState;
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ pub trait ApiRoutes {
|
|||||||
fn add_api_routes(self) -> Self;
|
fn add_api_routes(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TO_SEPARATOR: &str = "-to-";
|
const TO_SEPARATOR: &str = "_to_";
|
||||||
|
|
||||||
impl ApiRoutes for Router<AppState> {
|
impl ApiRoutes for Router<AppState> {
|
||||||
fn add_api_routes(self) -> Self {
|
fn add_api_routes(self) -> Self {
|
||||||
@@ -56,29 +56,29 @@ impl ApiRoutes for Router<AppState> {
|
|||||||
"/api/vecs/ids",
|
"/api/vecs/ids",
|
||||||
get(
|
get(
|
||||||
async |State(app_state): State<AppState>,
|
async |State(app_state): State<AppState>,
|
||||||
Query(pagination): Query<Pagination>|
|
Query(pagination): Query<PaginationParam>|
|
||||||
-> Response {
|
-> Response {
|
||||||
Json(app_state.interface.get_vecids(pagination)).into_response()
|
Json(app_state.interface.get_vecids(pagination)).into_response()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/vecs/indexes-to-ids",
|
"/api/vecs/index-to-ids",
|
||||||
get(
|
get(
|
||||||
async |State(app_state): State<AppState>,
|
async |State(app_state): State<AppState>,
|
||||||
Query(pagination): Query<Pagination>|
|
Query(paginated_index): Query<PaginatedIndexParam>|
|
||||||
-> Response {
|
-> Response {
|
||||||
Json(app_state.interface.get_indexes_to_vecids(pagination)).into_response()
|
Json(app_state.interface.get_index_to_vecids(paginated_index)).into_response()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/vecs/ids-to-indexes",
|
"/api/vecs/id-to-indexes",
|
||||||
get(
|
get(
|
||||||
async |State(app_state): State<AppState>,
|
async |State(app_state): State<AppState>,
|
||||||
Query(pagination): Query<Pagination>|
|
Query(param): Query<IdParam>|
|
||||||
-> Response {
|
-> Response {
|
||||||
Json(app_state.interface.get_vecids_to_indexes(pagination)).into_response()
|
Json(app_state.interface.get_vecid_to_indexes(param.id)).into_response()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -92,16 +92,18 @@ impl ApiRoutes for Router<AppState> {
|
|||||||
Query(params_opt): Query<ParamsOpt>,
|
Query(params_opt): Query<ParamsOpt>,
|
||||||
state: State<AppState>|
|
state: State<AppState>|
|
||||||
-> Response {
|
-> Response {
|
||||||
let variant = variant.replace("_", "-");
|
let variant = variant.replace("-", "_");
|
||||||
let mut split = variant.split(TO_SEPARATOR);
|
let mut split = variant.split(TO_SEPARATOR);
|
||||||
let params = Params::from((
|
|
||||||
(
|
if let Ok(index) = Index::try_from(split.next().unwrap()) {
|
||||||
Index::try_from(split.next().unwrap()).unwrap(),
|
let params = Params::from((
|
||||||
split.collect::<Vec<_>>().join(TO_SEPARATOR),
|
(index, split.collect::<Vec<_>>().join(TO_SEPARATOR)),
|
||||||
),
|
params_opt,
|
||||||
params_opt,
|
));
|
||||||
));
|
interface::handler(headers, Query(params), state).await
|
||||||
interface::handler(headers, Query(params), state).await
|
} else {
|
||||||
|
"Bad variant".into_response()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ impl Server {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve(self, watch: bool) -> color_eyre::Result<()> {
|
pub async fn serve(self, watch: bool, mcp: bool) -> color_eyre::Result<()> {
|
||||||
let state = self.0;
|
let state = self.0;
|
||||||
|
|
||||||
if let Some(websites_path) = state.websites_path.clone() {
|
if let Some(websites_path) = state.websites_path.clone() {
|
||||||
@@ -162,7 +162,7 @@ impl Server {
|
|||||||
let router = Router::new()
|
let router = Router::new()
|
||||||
.add_api_routes()
|
.add_api_routes()
|
||||||
.add_website_routes(state.website)
|
.add_website_routes(state.website)
|
||||||
.add_mcp_routes(state.interface)
|
.add_mcp_routes(state.interface, mcp)
|
||||||
.route("/version", get(Json(VERSION)))
|
.route("/version", get(Json(VERSION)))
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.layer(compression_layer)
|
.layer(compression_layer)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use brk_interface::{Interface, Pagination, Params};
|
use brk_interface::{IdParam, Interface, PaginatedIndexParam, PaginationParam, Params};
|
||||||
use rmcp::{
|
use brk_rmcp::{
|
||||||
Error as McpError, RoleServer, ServerHandler,
|
Error as McpError, RoleServer, ServerHandler,
|
||||||
model::{
|
model::{
|
||||||
CallToolResult, Content, Implementation, InitializeRequestParam, InitializeResult,
|
CallToolResult, Content, Implementation, InitializeRequestParam, InitializeResult,
|
||||||
@@ -8,6 +8,7 @@ use rmcp::{
|
|||||||
service::RequestContext,
|
service::RequestContext,
|
||||||
tool,
|
tool,
|
||||||
};
|
};
|
||||||
|
use log::info;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct API {
|
pub struct API {
|
||||||
@@ -23,88 +24,111 @@ impl API {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get the count of all existing indexes
|
Get the count of all existing indexes.
|
||||||
")]
|
")]
|
||||||
async fn get_index_count(&self) -> Result<CallToolResult, McpError> {
|
async fn get_index_count(&self) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_index_count");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_index_count()).unwrap(),
|
Content::json(self.interface.get_index_count()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get the count of all existing vec ids
|
Get the count of all existing vec ids.
|
||||||
")]
|
")]
|
||||||
async fn get_vecid_count(&self) -> Result<CallToolResult, McpError> {
|
async fn get_vecid_count(&self) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_vecid_count");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_vecid_count()).unwrap(),
|
Content::json(self.interface.get_vecid_count()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get the count of all existing vecs
|
Get the count of all existing vecs.
|
||||||
|
Equals to the sum of supported Indexes of each vec id.
|
||||||
")]
|
")]
|
||||||
async fn get_variant_count(&self) -> Result<CallToolResult, McpError> {
|
async fn get_variant_count(&self) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_variant_count");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_vec_count()).unwrap(),
|
Content::json(self.interface.get_vec_count()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get the list of all existing indexes
|
Get the list of all existing indexes.
|
||||||
")]
|
")]
|
||||||
async fn get_indexes(&self) -> Result<CallToolResult, McpError> {
|
async fn get_indexes(&self) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_indexes");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_indexes()).unwrap(),
|
Content::json(self.interface.get_indexes()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get an object which has all existing indexes as keys and a list of their accepted variants as values
|
Get an object which has all existing indexes as keys and a list of their accepted variants as values.
|
||||||
")]
|
")]
|
||||||
async fn get_accepted_indexes(&self) -> Result<CallToolResult, McpError> {
|
async fn get_accepted_indexes(&self) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_accepted_indexes");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_accepted_indexes()).unwrap(),
|
Content::json(self.interface.get_accepted_indexes()).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get the list of all existing vec ids
|
Get a paginated list of all existing vec ids.
|
||||||
|
There are up to 1,000 values per page.
|
||||||
|
If the `page` param is omitted, it will default to page `0`.
|
||||||
")]
|
")]
|
||||||
async fn get_vecids(
|
async fn get_vecids(
|
||||||
&self,
|
&self,
|
||||||
#[tool(aggr)] pagination: Pagination,
|
#[tool(aggr)] pagination: PaginationParam,
|
||||||
) -> Result<CallToolResult, McpError> {
|
) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_vecids");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_vecids(pagination)).unwrap(),
|
Content::json(self.interface.get_vecids(pagination)).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get an object which has all existing indexes as keys and a list of ids of vecs which support that index as values
|
Get a paginated list of all vec ids which support a given index.
|
||||||
|
There are up to 1,000 values per page.
|
||||||
|
If the `page` param is omitted, it will default to page `0`.
|
||||||
")]
|
")]
|
||||||
async fn get_indexes_to_vecids(
|
async fn get_index_to_vecids(
|
||||||
&self,
|
&self,
|
||||||
#[tool(aggr)] pagination: Pagination,
|
#[tool(aggr)] paginated_index: PaginatedIndexParam,
|
||||||
) -> Result<CallToolResult, McpError> {
|
) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_index_to_vecids");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_indexes_to_vecids(pagination)).unwrap(),
|
Content::json(self.interface.get_index_to_vecids(paginated_index)).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "
|
#[tool(description = "
|
||||||
Get an object which has all existing vec ids as keys and a list of indexes supported by that vec id as values
|
Get a list of all indexes supported by a given vec id.
|
||||||
|
The list will be empty if the vec id isn't correct.
|
||||||
")]
|
")]
|
||||||
async fn get_vecids_to_indexes(
|
async fn get_vecid_to_indexes(
|
||||||
&self,
|
&self,
|
||||||
#[tool(aggr)] pagination: Pagination,
|
#[tool(aggr)] param: IdParam,
|
||||||
) -> Result<CallToolResult, McpError> {
|
) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_vecid_to_indexes");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.get_vecids_to_indexes(pagination)).unwrap(),
|
Content::json(self.interface.get_vecid_to_indexes(param.id)).unwrap(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tool(description = "Get one or multiple vecs depending on given parameters")]
|
#[tool(description = "
|
||||||
|
Get one or multiple vecs depending on given parameters.
|
||||||
|
If you'd like to request multiple vec ids, simply separate them with a ','.
|
||||||
|
To get the last value set `-1` to the `from` parameter.
|
||||||
|
The response's format will depend on the given parameters, it will be:
|
||||||
|
- A value: If requested only one vec and the given range returns one value (for example: `from=-1`)
|
||||||
|
- A list: If requested only one vec and the given range returns multiple values (for example: `from=-1000&count=100` or `from=-444&to=-333`)
|
||||||
|
- A matrix: When multiple vecs are requested, even if they each return one value.
|
||||||
|
")]
|
||||||
fn get_vecs(&self, #[tool(aggr)] params: Params) -> Result<CallToolResult, McpError> {
|
fn get_vecs(&self, #[tool(aggr)] params: Params) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_vecs");
|
||||||
Ok(CallToolResult::success(vec![
|
Ok(CallToolResult::success(vec![
|
||||||
Content::json(self.interface.search_and_format(params).unwrap()).unwrap(),
|
Content::json(self.interface.search_and_format(params).unwrap()).unwrap(),
|
||||||
]))
|
]))
|
||||||
@@ -114,6 +138,7 @@ Get an object which has all existing vec ids as keys and a list of indexes suppo
|
|||||||
Get the running version of the Bitcoin Research Kit
|
Get the running version of the Bitcoin Research Kit
|
||||||
")]
|
")]
|
||||||
async fn get_version(&self) -> Result<CallToolResult, McpError> {
|
async fn get_version(&self) -> Result<CallToolResult, McpError> {
|
||||||
|
info!("mcp: get_version");
|
||||||
Ok(CallToolResult::success(vec![Content::text(format!(
|
Ok(CallToolResult::success(vec![Content::text(format!(
|
||||||
"v{VERSION}"
|
"v{VERSION}"
|
||||||
))]))
|
))]))
|
||||||
@@ -124,14 +149,22 @@ Get the running version of the Bitcoin Research Kit
|
|||||||
impl ServerHandler for API {
|
impl ServerHandler for API {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerInfo {
|
||||||
ServerInfo {
|
ServerInfo {
|
||||||
protocol_version: ProtocolVersion::V_2025_03_26,
|
protocol_version: ProtocolVersion::LATEST,
|
||||||
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
capabilities: ServerCapabilities::builder().enable_tools().build(),
|
||||||
server_info: Implementation::from_build_env(),
|
server_info: Implementation::from_build_env(),
|
||||||
instructions: Some(
|
instructions: Some(
|
||||||
"
|
"
|
||||||
This server provides an interface to communicate with a running instance of the Bitcoin Research Kit (brk).
|
This server provides an interface to communicate with a running instance of the Bitcoin Research Kit (also called brk or BRK).
|
||||||
Multiple tools are at your disposal including ones to fetch all sorts of Bitcoin on-chain data.
|
|
||||||
Arrays are also called Vectors (or Vecs).
|
Multiple tools are at your disposal including ones to fetch all sorts of Bitcoin on-chain metrics and market prices.
|
||||||
|
|
||||||
|
If you're unsure which datasets are available, try out different tools before browsing the web. Each tool gives important information about BRK's capabilities.
|
||||||
|
|
||||||
|
Vectors can also be called 'Vecs', 'Arrays' or 'Datasets', they can all be used interchangeably.
|
||||||
|
|
||||||
|
An 'Index' (or indexes) is the timeframe of a dataset.
|
||||||
|
|
||||||
|
'VecId' (or vecids) are the name of the dataset and what it represents.
|
||||||
"
|
"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
use axum::Router;
|
use axum::Router;
|
||||||
use brk_interface::Interface;
|
use brk_interface::Interface;
|
||||||
use rmcp::transport::{
|
use brk_rmcp::transport::{
|
||||||
StreamableHttpServerConfig,
|
StreamableHttpServerConfig,
|
||||||
streamable_http_server::{StreamableHttpService, session::local::LocalSessionManager},
|
streamable_http_server::{StreamableHttpService, session::local::LocalSessionManager},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
use api::*;
|
use api::*;
|
||||||
|
use log::info;
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
pub trait MCPRoutes {
|
pub trait MCPRoutes {
|
||||||
fn add_mcp_routes(self, interface: &'static Interface<'static>) -> Self;
|
fn add_mcp_routes(self, interface: &'static Interface<'static>, mcp: bool) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MCPRoutes for Router<AppState> {
|
impl MCPRoutes for Router<AppState> {
|
||||||
fn add_mcp_routes(self, interface: &'static Interface<'static>) -> Self {
|
fn add_mcp_routes(self, interface: &'static Interface<'static>, mcp: bool) -> Self {
|
||||||
|
if !mcp {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
let config = StreamableHttpServerConfig {
|
let config = StreamableHttpServerConfig {
|
||||||
// stateful_mode: false,
|
// stateful_mode: false, // breaks Claude
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,6 +32,8 @@ impl MCPRoutes for Router<AppState> {
|
|||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
info!("Setting MCP...");
|
||||||
|
|
||||||
self.nest_service("/mcp", service)
|
self.nest_service("/mcp", service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user