agentkit-tools-core 0.2.2

Tool traits, registry, execution, permissions, approvals, and auth for agentkit.
Documentation

agentkit-tools-core

Core abstractions for defining, registering, executing, and governing tools in agentkit.

This crate provides:

  • Tool trait — the interface every tool implements (spec + async invoke)
  • ToolRegistry — a name-keyed collection of tools
  • BasicToolExecutor — looks up tools, checks permissions, invokes them
  • Permission system — composable policies (PathPolicy, CommandPolicy, McpServerPolicy, CustomKindPolicy) combined via CompositePermissionChecker
  • Interruption typesApprovalRequest and AuthRequest for human-in-the-loop flows
  • Capability bridgeToolCapabilityProvider adapts a registry into the agentkit capability layer

Defining a tool

Implement the Tool trait with a ToolSpec and an async invoke method. If the tool performs operations that need permission checks (filesystem access, shell commands), override proposed_requests to declare them.

use agentkit_core::{MetadataMap, ToolOutput, ToolResultPart};
use agentkit_tools_core::{
    FileSystemPermissionRequest, PermissionRequest, Tool, ToolContext, ToolError,
    ToolName, ToolRequest, ToolResult, ToolSpec,
};
use async_trait::async_trait;
use serde_json::json;
use std::path::PathBuf;

/// A tool that reads a file and returns its contents.
struct ReadFileTool {
    spec: ToolSpec,
}

impl ReadFileTool {
    fn new() -> Self {
        Self {
            spec: ToolSpec::new(
                "read_file",
                "Read a file from the local filesystem",
                json!({
                    "type": "object",
                    "properties": {
                        "path": { "type": "string", "description": "Absolute file path" }
                    },
                    "required": ["path"]
                }),
            ),
        }
    }
}

#[async_trait]
impl Tool for ReadFileTool {
    fn spec(&self) -> &ToolSpec {
        &self.spec
    }

    // Declare that invoking this tool requires filesystem-read permission.
    fn proposed_requests(
        &self,
        request: &ToolRequest,
    ) -> Result<Vec<Box<dyn PermissionRequest>>, ToolError> {
        let path = request.input["path"]
            .as_str()
            .ok_or_else(|| ToolError::InvalidInput("missing `path`".into()))?;
        Ok(vec![Box::new(FileSystemPermissionRequest::Read {
            path: PathBuf::from(path),
            metadata: MetadataMap::new(),
        })])
    }

    async fn invoke(
        &self,
        request: ToolRequest,
        _ctx: &mut ToolContext<'_>,
    ) -> Result<ToolResult, ToolError> {
        let path = request.input["path"].as_str().unwrap();
        let content = std::fs::read_to_string(path)
            .map_err(|e| ToolError::ExecutionFailed(e.to_string()))?;
        Ok(ToolResult::new(ToolResultPart::success(
            request.call_id,
            ToolOutput::text(content),
        )))
    }
}

Registering tools and executing them

Build a ToolRegistry, wrap it in a BasicToolExecutor, and call execute with a ToolRequest and ToolContext.

use agentkit_capabilities::CapabilityContext;
use agentkit_core::{MetadataMap, SessionId, ToolOutput, ToolResultPart, TurnId};
use agentkit_tools_core::{
    BasicToolExecutor, PermissionChecker, PermissionDecision, Tool, ToolContext, ToolError,
    ToolExecutionOutcome, ToolExecutor, ToolName, ToolRegistry, ToolRequest, ToolResult, ToolSpec,
};
use async_trait::async_trait;
use serde_json::json;

// A minimal tool for demonstration.
struct EchoTool { spec: ToolSpec }

#[async_trait]
impl Tool for EchoTool {
    fn spec(&self) -> &ToolSpec { &self.spec }
    async fn invoke(
        &self, request: ToolRequest, _ctx: &mut ToolContext<'_>,
    ) -> Result<ToolResult, ToolError> {
        Ok(ToolResult::new(ToolResultPart::success(
            request.call_id,
            ToolOutput::structured(json!({ "ok": true })),
        )))
    }
}

// Permit everything — real agents should use CompositePermissionChecker.
struct AllowAll;
impl PermissionChecker for AllowAll {
    fn evaluate(
        &self, _request: &dyn agentkit_tools_core::PermissionRequest,
    ) -> PermissionDecision {
        PermissionDecision::Allow
    }
}

# #[tokio::main]
# async fn main() {
let registry = ToolRegistry::new().with(EchoTool {
    spec: ToolSpec::new("echo", "Return a fixed payload", json!({ "type": "object" })),
});

let executor = BasicToolExecutor::new(registry);
let metadata = MetadataMap::new();
let mut ctx = ToolContext {
    capability: CapabilityContext {
        session_id: Some(&SessionId::new("s1")),
        turn_id: Some(&TurnId::new("t1")),
        metadata: &metadata,
    },
    permissions: &AllowAll,
    resources: &(),
    cancellation: None,
};

let outcome = executor
    .execute(
        ToolRequest::new(
            "call-1",
            "echo",
            json!({}),
            SessionId::new("s1"),
            TurnId::new("t1"),
        ),
        &mut ctx,
    )
    .await;

assert!(matches!(outcome, ToolExecutionOutcome::Completed(_)));
# }

Setting up permissions

Use CompositePermissionChecker to layer multiple policies. Policies are evaluated in order; the first Deny short-circuits. Any RequireApproval is returned unless a later policy denies.

use agentkit_tools_core::{
    CommandPolicy, CompositePermissionChecker, PathPolicy,
    McpServerPolicy, PermissionDecision,
};

let permissions = CompositePermissionChecker::new(PermissionDecision::Allow)
    // Allow filesystem access under the workspace; protect .env files.
    .with_policy(
        PathPolicy::new()
            .allow_root("/workspace/project")
            .protect_root("/workspace/project/.env"),
    )
    // Allow git and cargo; require approval for anything else.
    .with_policy(
        CommandPolicy::new()
            .allow_executable("git")
            .allow_executable("cargo")
            .deny_executable("rm")
            .require_approval_for_unknown(true),
    )
    // Trust a specific MCP server.
    .with_policy(
        McpServerPolicy::new()
            .trust_server("github-mcp"),
    );