pub struct ToolDecl {
pub name: String,
pub description: Option<String>,
pub sql: Option<String>,
pub path: Option<String>,
pub method: Option<String>,
pub base_url: Option<String>,
pub script: Option<String>,
pub ui_resource_uri: Option<String>,
pub parameters: Vec<ParamDecl>,
pub annotations: Option<AnnotationsDecl>,
}Expand description
Single [[tools]] entry — a declaratively-defined tool surface.
Fields§
§name: StringTool name (required for production via ServerConfig::validate).
description: Option<String>Human-readable tool description.
sql: Option<String>SQL template (uses :param placeholders bound by ParamDecl).
path: Option<String>HTTP request path for a single-call OpenAPI/REST tool (D-01), e.g.
"/Line/Mode/tube/Status". Concatenated onto the backend base_url
(or this tool’s Self::base_url override). Additive per REF-01 — None
for SQL / script tools.
method: Option<String>HTTP method for a single-call tool ("GET", "POST", …). Pairs with
Self::path (D-01). Additive; None for SQL / script tools.
base_url: Option<String>Per-tool backend base-URL override. When absent a single-call tool
inherits [backend].base_url. Additive; None for SQL / script tools.
script: Option<String>JavaScript body for a script tool (D-01) — a code-mode snippet that
orchestrates multiple backend calls and binds [[tools.parameters]] to
args. When set, this entry is a script tool (Self::is_script_tool).
Additive; None for SQL / single-call tools.
ui_resource_uri: Option<String>Optional UI-resource URI for structuredContent widgets.
parameters: Vec<ParamDecl>[[tools.parameters]] — declared input parameters.
annotations: Option<AnnotationsDecl>[tools.annotations] — MCP toolAnnotations.
Implementations§
Source§impl ToolDecl
impl ToolDecl
Sourcepub fn is_script_tool(&self) -> bool
pub fn is_script_tool(&self) -> bool
Whether this [[tools]] entry is a script tool (D-01 detection rule).
The detection rule is: script.is_some() ⇒ script tool; otherwise a
path + method pair ⇒ single-call HTTP tool; otherwise (a sql
field) ⇒ SQL tool. Plan 03/05 synthesizers branch on this method so the
rule lives in exactly one place. Mutual-exclusivity is enforced at
ServerConfig::validate (an entry mixing kinds is rejected, not
silently resolved by precedence).
§Examples
use pmcp_server_toolkit::config::ToolDecl;
let script = ToolDecl { script: Some("await api.get('/x')".into()), ..Default::default() };
assert!(script.is_script_tool());
let single = ToolDecl {
path: Some("/Line/Mode/tube/Status".into()),
method: Some("GET".into()),
..Default::default()
};
assert!(!single.is_script_tool());Sourcepub fn param_position(&self, param_name: &str) -> ParamPosition
pub fn param_position(&self, param_name: &str) -> ParamPosition
Where param_name sits for the purposes of the D3 default length cap
(Phase 128).
§Derivation
- The name appears as a
{name}segment ofSelf::path->ParamPosition::Path. - Else
Self::methodcarries a request body (method_carries_request_body:POST,PUT,PATCH) ->ParamPosition::Body. - Else there IS a method ->
ParamPosition::Query. A method with no request body has nowhere but the URL to carry a non-path input,OPTIONSincluded. - Else (no method at all) ->
ParamPosition::Body: a SQL named bind or a script-tool argument.
§Coupling with tools.rs::build_operation — structural, not documentary
Both of this function’s splits agree with build_operation because both
read the same helper, not because a comment says so:
- the PATH arm and
build_operation’spath_param_namesboth readpath_placeholder_names; - the QUERY/BODY arms and
build_operation’sParameterLocationassignment both readmethod_carries_request_body, which is also the sole source ofOperation::has_request_body.
The second agreement is Phase 128 CR-02/CR-03, and it replaced a documented
“deliberate divergence” that did not survive measurement. build_operation
used to mark every non-path declared parameter ParameterLocation::Query
regardless of method while this function answered Body for a mutating tool
— justified on the grounds that the two questions (“where does the value
TRAVEL” versus “where is LENGTH dangerous”) are different. They are, but the
answers were both wrong: the value travelled in the query string, so the D3
cap was withheld from a genuine request-line input on the stated grounds
that it was a payload field, and build_body — which collected only args
absent from operation.parameters — sent no payload at all.
D-05 is still honoured, and now BY the routing rather than despite it: a
Body position means the value really is a JSON payload field, and
default_cap_applies emits no default maxLength for it. The escape for
a long Query value remains an explicit per-parameter max_length.
§Examples
use pmcp_server_toolkit::config::{ParamPosition, ToolDecl};
let search = ToolDecl {
path: Some("/lines/{line_id}/status".into()),
method: Some("GET".into()),
..Default::default()
};
assert_eq!(search.param_position("line_id"), ParamPosition::Path);
assert_eq!(search.param_position("detail"), ParamPosition::Query);
let comment = ToolDecl {
path: Some("/issues/{id}/comments".into()),
method: Some("POST".into()),
..Default::default()
};
assert_eq!(comment.param_position("id"), ParamPosition::Path);
// Free text on a mutating method is NOT capped — D-05.
assert_eq!(comment.param_position("body_text"), ParamPosition::Body);
let sql = ToolDecl { sql: Some("SELECT 1".into()), ..Default::default() };
assert_eq!(sql.param_position("anything"), ParamPosition::Body);Trait Implementations§
Source§impl<'de> Deserialize<'de> for ToolDecl
impl<'de> Deserialize<'de> for ToolDecl
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl StructuralPartialEq for ToolDecl
Auto Trait Implementations§
impl Freeze for ToolDecl
impl RefUnwindSafe for ToolDecl
impl Send for ToolDecl
impl Sync for ToolDecl
impl Unpin for ToolDecl
impl UnsafeUnpin for ToolDecl
impl UnwindSafe for ToolDecl
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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