Skip to main content

McpManager

Struct McpManager 

Source
pub struct McpManager {
    pub servers: HashMap<String, RunningService<RoleClient, AgentBlockClientHandler>>,
    pub handler: AgentBlockClientHandler,
    /* private fields */
}

Fields§

§servers: HashMap<String, RunningService<RoleClient, AgentBlockClientHandler>>

Server connections keyed by name. pub(crate) so integration tests can insert in-process test servers directly (same as concurrency_tests in this module).

§handler: AgentBlockClientHandler

Shared handler instance — all connections share the same registry Arc.

Implementations§

Source§

impl McpManager

Source

pub fn new() -> Self

Source

pub fn with_rpc_timeout(rpc_timeout: Duration) -> BlockResult<Self>

Construct a manager with a caller-specified RPC timeout. Applies to connect, list_tools, and call_tool alike.

rpc_timeout must be non-zero. Duration::ZERO would cause every tokio::time::timeout to fire immediately, silently turning every MCP round-trip into a timeout error — for an autonomous agent that is a “everything looks broken” failure mode. We reject it at construction time so the misconfiguration surfaces loudly at startup instead of being swallowed at the first RPC.

Source

pub async fn connect( &mut self, name: &str, command: &str, args: &[String], trace_context: bool, cwd: Option<&Path>, ) -> BlockResult<()>

Spawn the MCP server process and complete the MCP initialize handshake.

trace_context: if true, __ab_obs observability context will be injected into call_tool arguments for this server. Defaults to false (opt-in) so that third-party / untrusted stdio servers do not receive agent identity metadata unless explicitly enabled.

cwd: if Some, the spawned subprocess inherits this as its current working directory; if None, the subprocess inherits the parent process’s CWD. Callers driven through agent-block-core typically pass BlockConfig.project_root so the MCP server sees the same project root as the Lua script (matters for servers that rely on path-based discovery such as git rev-parse --show-toplevel).

Source

pub async fn list_tools(&self, name: &str) -> BlockResult<Value>

Call tools/list and return the tools as a JSON array.

Immutable receiver so concurrent readers can share an RwLock<McpManager>.

Source

pub async fn call_tool( &self, name: &str, tool_name: &str, arguments: Value, ) -> BlockResult<Value>

Call tools/call with the given tool name and arguments.

Returns the full rmcp CallToolResult serialized to JSON ({"content": [...], "isError": bool, ...}) on success, including the isError flag — tool-execution errors are passed through to the caller, following the MCP spec’s intent that the LLM sees them and self-corrects. Only protocol / transport / timeout failures surface as Err(BlockError::*).

arguments must be a JSON Object or Null. Null is treated as “no arguments”; any other shape (array, scalar) returns an error rather than silently dropping the payload. Immutable receiver so concurrent readers can share an RwLock<McpManager>.

Source

pub async fn disconnect(&mut self, name: &str) -> BlockResult<()>

Cancel the named server and remove it from the manager.

The server is removed from the internal map before the cancel round-trip begins, so a slow or failed cancel never leaves a zombie entry behind. If graceful cancel exceeds rpc_timeout, the service handle is dropped at the end of the match arm — rmcp’s Drop impl cancels the peer’s cancellation token, which terminates the internal task and closes the transport — and BlockError::Timeout is returned.

The same rpc_timeout is reused here so callers have a single knob governing every MCP round-trip (see with_rpc_timeout).

Callers may re-connect the same name safely after any outcome.

Source

pub async fn disconnect_all(&mut self) -> BlockResult<()>

Cancel all managed servers.

Every server is disconnected regardless of individual failures. The first error encountered is returned so shutdown can signal a problem; subsequent errors are logged at warn level so they are not silently discarded.

Source

pub fn set_handler_isle(&mut self, isle: Arc<AsyncIsle>)

Wire the handler Isle into this manager’s AgentBlockClientHandler.

Must be called after both the McpManager and the AsyncIsle are constructed. The handler Isle is used to dispatch Lua notification callbacks (on_progress etc.) from the rmcp task thread.

Idempotent: a second call replaces the previous Isle reference.

Source

pub fn set_main_isle(&mut self, isle: Arc<AsyncIsle>)

Wire the main Isle into the shared AgentBlockClientHandler.

Must be called after construction and before connect / connect_http so that progress/log notification dispatchers can call user Lua callbacks stored in the main Isle’s globals (upvalue-safe path).

Also starts the bounded notification dispatch task (M-3: capacity-128 channel that prevents unbounded memory growth from chatty notification sources).

Idempotent: a second call replaces the previous Isle reference and restarts the dispatch task on the new channel.

Source

pub async fn connect_http( &mut self, name: &str, url: &str, opts: Value, ) -> BlockResult<()>

Connect to an MCP server via Streamable HTTP transport.

opts may contain:

  • auth_header (string): bearer-token authentication header value.
  • trace_context (bool): if true, inject __ab_obs observability context into call_tool arguments. Default: false (opt-in).

The handler Isle must be wired via set_handler_isle before calling this method if on_progress callbacks are needed.

Source

pub async fn list_resources(&self, name: &str) -> BlockResult<Value>

Call resources/list and return resources as a JSON array.

Immutable receiver — usable under RwLock::read alongside concurrent RPCs.

Source

pub async fn list_resource_templates(&self, name: &str) -> BlockResult<Value>

Call resources/templates/list and return resource templates as a JSON array.

Immutable receiver — usable under RwLock::read alongside concurrent RPCs.

Source

pub async fn ping(&self, name: &str) -> BlockResult<u64>

Send a ping keepalive to the named server and return the round-trip latency in milliseconds.

Uses send_request(ClientRequest::PingRequest(...)) — rmcp 1.4.0 has no dedicated Peer::ping() method. Latency is measured with Instant::now() immediately before the send and elapsed() immediately after the EmptyResult is received (crux must_not_simplify).

Immutable receiver — usable under RwLock::read alongside concurrent RPCs.

Source

pub async fn read_resource(&self, name: &str, uri: &str) -> BlockResult<Value>

Call resources/read and return the resource contents as JSON.

Immutable receiver — usable under RwLock::read.

Source

pub async fn subscribe_resource(&self, name: &str, uri: &str) -> BlockResult<()>

Call resources/subscribe to subscribe to updates for the given URI.

Immutable receiver — usable under RwLock::read.

Source

pub async fn unsubscribe_resource( &self, name: &str, uri: &str, ) -> BlockResult<()>

Call resources/unsubscribe to stop receiving updates for the given URI.

Immutable receiver — usable under RwLock::read.

Source

pub async fn list_prompts(&self, name: &str) -> BlockResult<Value>

Call prompts/list and return prompts as a JSON array.

Immutable receiver — usable under RwLock::read.

Source

pub async fn get_prompt( &self, name: &str, prompt_name: &str, args: Value, ) -> BlockResult<Value>

Call prompts/get with the given prompt name and optional arguments.

args must be a JSON Object or Null. Immutable receiver.

Source

pub async fn complete( &self, name: &str, ref_json: Value, arg_name: &str, arg_value: &str, ) -> BlockResult<Value>

Call completion/complete with the given reference and argument.

ref_json must be a JSON Object with a type field of either "ref/prompt" (with a name field) or "ref/resource" (with a uri field). Any other type value is rejected with BlockError::Mcp.

CompletionContext is not exposed (scope-out per issue.md:51); it is always sent as None. Immutable receiver — usable under RwLock::read.

Source

pub fn server_info(&self, name: &str) -> BlockResult<Value>

Return the server’s InitializeResult serialized as JSON.

peer_info() is sync (no I/O). It returns Some after a successful MCP handshake and None before initialization completes.

Immutable receiver — usable under RwLock::read.

Source

pub fn send_cancelled(&self, name: &str, request_id: Option<i64>)

Send a notifications/cancelled to the named server.

This is a best-effort fire-and-forget: the notification is spawned in a separate task so the caller is not blocked waiting for transport ack. Errors from the peer send are logged at warn level and discarded — the MCP spec does not require the server to ack cancellations (fire-and-forget by design; warn-level logging is intentional).

request_id is Some(id) when the caller has captured the rmcp-internal request ID, or None when the ID is not available (e.g. a timeout fired before the ID was obtained). When None the notification is skipped entirely to avoid accidentally matching request ID 0 on a server that allocates IDs starting from zero.

Source

pub fn notify_roots_list_changed(&self, name: &str)

Notify the named server that the client’s roots list has changed.

Sends a notifications/roots/list_changed notification to the server as a fire-and-forget operation. The server may respond by issuing a new roots/list request.

§Arguments
  • name — the name of the server connection to notify.
§Errors

None propagated. Unknown server is logged at warn level and silently ignored. Send failures inside the spawned task are also logged at warn level and discarded.

Trait Implementations§

Source§

impl Default for McpManager

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> MaybeSend for T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more