Skip to main content

InMemoryMCPToolServer

Struct InMemoryMCPToolServer 

Source
pub struct InMemoryMCPToolServer { /* private fields */ }

Implementations§

Source§

impl InMemoryMCPToolServer

Source

pub fn new() -> Self

Examples found in repository?
examples/mcp_governance.rs (line 36)
33fn main() -> agentledger::Result<()> {
34    let mut runtime = Runtime::new();
35    runtime.set_sandbox(Box::new(DemoSandboxExecutor));
36    let mut server = InMemoryMCPToolServer::new();
37    server.add_tool(
38        state(&[
39            ("name", "mcp.github.create_pr".into()),
40            (
41                "inputSchema",
42                Value::Object(state(&[
43                    ("type", "object".into()),
44                    (
45                        "required",
46                        Value::Array(vec![Value::String("title".to_string())]),
47                    ),
48                    (
49                        "properties",
50                        Value::Object(state(&[(
51                            "title",
52                            Value::Object(state(&[
53                                ("type", "string".into()),
54                                ("minLength", 1_i64.into()),
55                            ])),
56                        )])),
57                    ),
58                ])),
59            ),
60            (
61                "annotations",
62                Value::Object(state(&[
63                    ("side_effect", "external_write".into()),
64                    ("risk_level", "high".into()),
65                    ("idempotency_required", true.into()),
66                    ("approval_required", true.into()),
67                    ("sandbox_required", true.into()),
68                    (
69                        "sandbox_policy",
70                        Value::Object(state(&[
71                            ("network", "deny".into()),
72                            ("filesystem", "read-only".into()),
73                        ])),
74                    ),
75                ])),
76            ),
77        ]),
78        create_pr,
79    );
80    let adapter = MCPToolAdapter { client_call: create_pr };
81    for descriptor in server.list_tools() {
82        runtime.register_tool(adapter.tool_spec_from_descriptor(&descriptor));
83    }
84
85    let (run_id, _) = runtime.create_run(State::new());
86    let first_claim = runtime.store.claim_step("worker-before-approval", &run_id, 60.0)?;
87    let first_ctx = AgentContext {
88        run_id: run_id.clone(),
89        session_id: first_claim.session_id.clone(),
90        step_id: first_claim.step_id.clone(),
91        agent_role: "MCPAgent".to_string(),
92        lease_token: first_claim.lease_token.clone(),
93        attempt: first_claim.attempt,
94        state_version: first_claim.state_version,
95        pending_patch: State::new(),
96    };
97    let first = runtime.call_tool(
98        &first_ctx,
99        "mcp.github.create_pr",
100        state(&[
101            ("title", "Update runtime docs".into()),
102            ("_logical_operation", "docs-pr".into()),
103        ]),
104    );
105    let approval_id = first
106        .unwrap_err()
107        .0
108        .trim_start_matches("approval required:")
109        .to_string();
110    runtime.store.mark_waiting_human(
111        &run_id,
112        &first_ctx.step_id,
113        "approval required for tool mcp.github.create_pr",
114        &approval_id,
115    );
116    runtime
117        .store
118        .approve_request(&approval_id, "maintainer", "demo approval")?;
119
120    let second_claim = runtime.store.claim_step("worker-after-approval", &run_id, 60.0)?;
121    let mut second_ctx = AgentContext {
122        run_id: run_id.clone(),
123        session_id: second_claim.session_id.clone(),
124        step_id: second_claim.step_id.clone(),
125        agent_role: "MCPAgent".to_string(),
126        lease_token: second_claim.lease_token.clone(),
127        attempt: second_claim.attempt,
128        state_version: second_claim.state_version,
129        pending_patch: State::new(),
130    };
131    let result = runtime.call_tool(
132        &second_ctx,
133        "mcp.github.create_pr",
134        state(&[
135            ("title", "Update runtime docs".into()),
136            ("_logical_operation", "docs-pr".into()),
137        ]),
138    )?;
139    second_ctx.write_state("pull_request", result);
140    runtime.store.commit_state_patch(
141        &run_id,
142        &second_ctx.step_id,
143        &second_ctx.lease_token,
144        second_ctx.state_version,
145        second_ctx.pending_patch,
146    )?;
147
148    let approvals = runtime.store.approval_requests(&run_id);
149    println!(
150        "{{\n  \"run_id\": \"{}\",\n  \"first_attempt_waited_for_approval\": true,\n  \"second_attempt_ok\": true,\n  \"approval_count\": {},\n  \"approval_status\": \"{}\",\n  \"external_action_count\": 1,\n  \"final_state_has_pull_request\": {}\n}}",
151        run_id,
152        approvals.len(),
153        approvals.first().map(|row| row.status.as_str()).unwrap_or(""),
154        runtime
155            .store
156            .final_state(&run_id)?
157            .contains_key("pull_request")
158    );
159    Ok(())
160}
Source

pub fn add_tool(&mut self, descriptor: State, handler: MCPCall)

Examples found in repository?
examples/mcp_governance.rs (lines 37-79)
33fn main() -> agentledger::Result<()> {
34    let mut runtime = Runtime::new();
35    runtime.set_sandbox(Box::new(DemoSandboxExecutor));
36    let mut server = InMemoryMCPToolServer::new();
37    server.add_tool(
38        state(&[
39            ("name", "mcp.github.create_pr".into()),
40            (
41                "inputSchema",
42                Value::Object(state(&[
43                    ("type", "object".into()),
44                    (
45                        "required",
46                        Value::Array(vec![Value::String("title".to_string())]),
47                    ),
48                    (
49                        "properties",
50                        Value::Object(state(&[(
51                            "title",
52                            Value::Object(state(&[
53                                ("type", "string".into()),
54                                ("minLength", 1_i64.into()),
55                            ])),
56                        )])),
57                    ),
58                ])),
59            ),
60            (
61                "annotations",
62                Value::Object(state(&[
63                    ("side_effect", "external_write".into()),
64                    ("risk_level", "high".into()),
65                    ("idempotency_required", true.into()),
66                    ("approval_required", true.into()),
67                    ("sandbox_required", true.into()),
68                    (
69                        "sandbox_policy",
70                        Value::Object(state(&[
71                            ("network", "deny".into()),
72                            ("filesystem", "read-only".into()),
73                        ])),
74                    ),
75                ])),
76            ),
77        ]),
78        create_pr,
79    );
80    let adapter = MCPToolAdapter { client_call: create_pr };
81    for descriptor in server.list_tools() {
82        runtime.register_tool(adapter.tool_spec_from_descriptor(&descriptor));
83    }
84
85    let (run_id, _) = runtime.create_run(State::new());
86    let first_claim = runtime.store.claim_step("worker-before-approval", &run_id, 60.0)?;
87    let first_ctx = AgentContext {
88        run_id: run_id.clone(),
89        session_id: first_claim.session_id.clone(),
90        step_id: first_claim.step_id.clone(),
91        agent_role: "MCPAgent".to_string(),
92        lease_token: first_claim.lease_token.clone(),
93        attempt: first_claim.attempt,
94        state_version: first_claim.state_version,
95        pending_patch: State::new(),
96    };
97    let first = runtime.call_tool(
98        &first_ctx,
99        "mcp.github.create_pr",
100        state(&[
101            ("title", "Update runtime docs".into()),
102            ("_logical_operation", "docs-pr".into()),
103        ]),
104    );
105    let approval_id = first
106        .unwrap_err()
107        .0
108        .trim_start_matches("approval required:")
109        .to_string();
110    runtime.store.mark_waiting_human(
111        &run_id,
112        &first_ctx.step_id,
113        "approval required for tool mcp.github.create_pr",
114        &approval_id,
115    );
116    runtime
117        .store
118        .approve_request(&approval_id, "maintainer", "demo approval")?;
119
120    let second_claim = runtime.store.claim_step("worker-after-approval", &run_id, 60.0)?;
121    let mut second_ctx = AgentContext {
122        run_id: run_id.clone(),
123        session_id: second_claim.session_id.clone(),
124        step_id: second_claim.step_id.clone(),
125        agent_role: "MCPAgent".to_string(),
126        lease_token: second_claim.lease_token.clone(),
127        attempt: second_claim.attempt,
128        state_version: second_claim.state_version,
129        pending_patch: State::new(),
130    };
131    let result = runtime.call_tool(
132        &second_ctx,
133        "mcp.github.create_pr",
134        state(&[
135            ("title", "Update runtime docs".into()),
136            ("_logical_operation", "docs-pr".into()),
137        ]),
138    )?;
139    second_ctx.write_state("pull_request", result);
140    runtime.store.commit_state_patch(
141        &run_id,
142        &second_ctx.step_id,
143        &second_ctx.lease_token,
144        second_ctx.state_version,
145        second_ctx.pending_patch,
146    )?;
147
148    let approvals = runtime.store.approval_requests(&run_id);
149    println!(
150        "{{\n  \"run_id\": \"{}\",\n  \"first_attempt_waited_for_approval\": true,\n  \"second_attempt_ok\": true,\n  \"approval_count\": {},\n  \"approval_status\": \"{}\",\n  \"external_action_count\": 1,\n  \"final_state_has_pull_request\": {}\n}}",
151        run_id,
152        approvals.len(),
153        approvals.first().map(|row| row.status.as_str()).unwrap_or(""),
154        runtime
155            .store
156            .final_state(&run_id)?
157            .contains_key("pull_request")
158    );
159    Ok(())
160}
Source

pub fn list_tools(&self) -> Vec<State>

Examples found in repository?
examples/mcp_governance.rs (line 81)
33fn main() -> agentledger::Result<()> {
34    let mut runtime = Runtime::new();
35    runtime.set_sandbox(Box::new(DemoSandboxExecutor));
36    let mut server = InMemoryMCPToolServer::new();
37    server.add_tool(
38        state(&[
39            ("name", "mcp.github.create_pr".into()),
40            (
41                "inputSchema",
42                Value::Object(state(&[
43                    ("type", "object".into()),
44                    (
45                        "required",
46                        Value::Array(vec![Value::String("title".to_string())]),
47                    ),
48                    (
49                        "properties",
50                        Value::Object(state(&[(
51                            "title",
52                            Value::Object(state(&[
53                                ("type", "string".into()),
54                                ("minLength", 1_i64.into()),
55                            ])),
56                        )])),
57                    ),
58                ])),
59            ),
60            (
61                "annotations",
62                Value::Object(state(&[
63                    ("side_effect", "external_write".into()),
64                    ("risk_level", "high".into()),
65                    ("idempotency_required", true.into()),
66                    ("approval_required", true.into()),
67                    ("sandbox_required", true.into()),
68                    (
69                        "sandbox_policy",
70                        Value::Object(state(&[
71                            ("network", "deny".into()),
72                            ("filesystem", "read-only".into()),
73                        ])),
74                    ),
75                ])),
76            ),
77        ]),
78        create_pr,
79    );
80    let adapter = MCPToolAdapter { client_call: create_pr };
81    for descriptor in server.list_tools() {
82        runtime.register_tool(adapter.tool_spec_from_descriptor(&descriptor));
83    }
84
85    let (run_id, _) = runtime.create_run(State::new());
86    let first_claim = runtime.store.claim_step("worker-before-approval", &run_id, 60.0)?;
87    let first_ctx = AgentContext {
88        run_id: run_id.clone(),
89        session_id: first_claim.session_id.clone(),
90        step_id: first_claim.step_id.clone(),
91        agent_role: "MCPAgent".to_string(),
92        lease_token: first_claim.lease_token.clone(),
93        attempt: first_claim.attempt,
94        state_version: first_claim.state_version,
95        pending_patch: State::new(),
96    };
97    let first = runtime.call_tool(
98        &first_ctx,
99        "mcp.github.create_pr",
100        state(&[
101            ("title", "Update runtime docs".into()),
102            ("_logical_operation", "docs-pr".into()),
103        ]),
104    );
105    let approval_id = first
106        .unwrap_err()
107        .0
108        .trim_start_matches("approval required:")
109        .to_string();
110    runtime.store.mark_waiting_human(
111        &run_id,
112        &first_ctx.step_id,
113        "approval required for tool mcp.github.create_pr",
114        &approval_id,
115    );
116    runtime
117        .store
118        .approve_request(&approval_id, "maintainer", "demo approval")?;
119
120    let second_claim = runtime.store.claim_step("worker-after-approval", &run_id, 60.0)?;
121    let mut second_ctx = AgentContext {
122        run_id: run_id.clone(),
123        session_id: second_claim.session_id.clone(),
124        step_id: second_claim.step_id.clone(),
125        agent_role: "MCPAgent".to_string(),
126        lease_token: second_claim.lease_token.clone(),
127        attempt: second_claim.attempt,
128        state_version: second_claim.state_version,
129        pending_patch: State::new(),
130    };
131    let result = runtime.call_tool(
132        &second_ctx,
133        "mcp.github.create_pr",
134        state(&[
135            ("title", "Update runtime docs".into()),
136            ("_logical_operation", "docs-pr".into()),
137        ]),
138    )?;
139    second_ctx.write_state("pull_request", result);
140    runtime.store.commit_state_patch(
141        &run_id,
142        &second_ctx.step_id,
143        &second_ctx.lease_token,
144        second_ctx.state_version,
145        second_ctx.pending_patch,
146    )?;
147
148    let approvals = runtime.store.approval_requests(&run_id);
149    println!(
150        "{{\n  \"run_id\": \"{}\",\n  \"first_attempt_waited_for_approval\": true,\n  \"second_attempt_ok\": true,\n  \"approval_count\": {},\n  \"approval_status\": \"{}\",\n  \"external_action_count\": 1,\n  \"final_state_has_pull_request\": {}\n}}",
151        run_id,
152        approvals.len(),
153        approvals.first().map(|row| row.status.as_str()).unwrap_or(""),
154        runtime
155            .store
156            .final_state(&run_id)?
157            .contains_key("pull_request")
158    );
159    Ok(())
160}
Source

pub fn call_tool(&self, name: &str, args: State) -> Result<Value>

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, 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, 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.