#
tokens: 49248/50000 20/231 files (page 3/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 3 of 8. Use http://codebase.md/tuananh/hyper-mcp?page={x} to view the full context.

# Directory Structure

```
├── .cursor
│   └── rules
│       └── print-ctx-size.mdc
├── .dockerignore
├── .github
│   ├── renovate.json5
│   └── workflows
│       ├── ci.yml
│       ├── nightly.yml
│       └── release.yml
├── .gitignore
├── .gitmodules
├── .hadolint.yaml
├── .pre-commit-config.yaml
├── .windsurf
│   └── rules
│       ├── print-ctx-size.md
│       └── think.md
├── assets
│   ├── cursor-mcp-1.png
│   ├── cursor-mcp.png
│   ├── eval-py.jpg
│   └── logo.png
├── Cargo.lock
├── Cargo.toml
├── config.example.json
├── config.example.yaml
├── CREATING_PLUGINS.md
├── DEPLOYMENT.md
├── Dockerfile
├── examples
│   └── plugins
│       ├── v1
│       │   ├── arxiv
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── context7
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── crates-io
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── crypto-price
│       │   │   ├── Dockerfile
│       │   │   ├── go.mod
│       │   │   ├── go.sum
│       │   │   ├── main.go
│       │   │   ├── pdk.gen.go
│       │   │   └── README.md
│       │   ├── eval-py
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── fetch
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── fs
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── github
│       │   │   ├── .gitignore
│       │   │   ├── branches.go
│       │   │   ├── Dockerfile
│       │   │   ├── files.go
│       │   │   ├── gists.go
│       │   │   ├── go.mod
│       │   │   ├── go.sum
│       │   │   ├── issues.go
│       │   │   ├── main.go
│       │   │   ├── pdk.gen.go
│       │   │   ├── README.md
│       │   │   └── repo.go
│       │   ├── gitlab
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── gomodule
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── hash
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.lock
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── maven
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── meme-generator
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── generate_embedded.py
│       │   │   ├── README.md
│       │   │   ├── src
│       │   │   │   ├── embedded.rs
│       │   │   │   ├── lib.rs
│       │   │   │   └── pdk.rs
│       │   │   └── templates.json
│       │   ├── memory
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── myip
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.lock
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── qdrant
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       ├── pdk.rs
│       │   │       └── qdrant_client.rs
│       │   ├── qr-code
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.lock
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── serper
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── sqlite
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── think
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   └── src
│       │   │       ├── lib.rs
│       │   │       └── pdk.rs
│       │   ├── time
│       │   │   ├── .cargo
│       │   │   │   └── config.toml
│       │   │   ├── .gitignore
│       │   │   ├── Cargo.toml
│       │   │   ├── Dockerfile
│       │   │   ├── README.md
│       │   │   ├── src
│       │   │   │   ├── lib.rs
│       │   │   │   └── pdk.rs
│       │   │   └── time.wasm
│       │   └── tool-list-changed
│       │       ├── .gitignore
│       │       ├── Cargo.toml
│       │       ├── Dockerfile
│       │       ├── README.md
│       │       ├── src
│       │       │   ├── lib.rs
│       │       │   └── pdk.rs
│       │       └── tool_list_changed.wasm
│       └── v2
│           └── rstime
│               ├── .cargo
│               │   └── config.toml
│               ├── .gitignore
│               ├── Cargo.toml
│               ├── Dockerfile
│               ├── README.md
│               ├── rstime.wasm
│               └── src
│                   ├── lib.rs
│                   └── pdk
│                       ├── exports.rs
│                       ├── imports.rs
│                       ├── mod.rs
│                       └── types.rs
├── iac
│   ├── .terraform.lock.hcl
│   ├── main.tf
│   ├── outputs.tf
│   └── variables.tf
├── justfile
├── LICENSE
├── README.md
├── RUNTIME_CONFIG.md
├── rust-toolchain.toml
├── server.json
├── SKIP_TOOLS_GUIDE.md
├── src
│   ├── cli.rs
│   ├── config.rs
│   ├── https_auth.rs
│   ├── logging.rs
│   ├── main.rs
│   ├── naming.rs
│   ├── plugin.rs
│   ├── service.rs
│   └── wasm
│       ├── http.rs
│       ├── mod.rs
│       ├── oci.rs
│       └── s3.rs
├── templates
│   └── plugins
│       ├── go
│       │   ├── .gitignore
│       │   ├── Dockerfile
│       │   ├── exports.go
│       │   ├── go.mod
│       │   ├── go.sum
│       │   ├── imports.go
│       │   ├── main.go
│       │   ├── README.md
│       │   └── types.go
│       ├── README.md
│       └── rust
│           ├── .cargo
│           │   └── config.toml
│           ├── .gitignore
│           ├── Cargo.toml
│           ├── Dockerfile
│           ├── README.md
│           └── src
│               ├── lib.rs
│               └── pdk
│                   ├── exports.rs
│                   ├── imports.rs
│                   ├── mod.rs
│                   └── types.rs
├── tests
│   └── fixtures
│       ├── config_with_auths.json
│       ├── config_with_auths.yaml
│       ├── documentation_example.json
│       ├── documentation_example.yaml
│       ├── invalid_auth_config.yaml
│       ├── invalid_plugin_name.yaml
│       ├── invalid_structure.yaml
│       ├── invalid_url.yaml
│       ├── keyring_auth_config.yaml
│       ├── skip_tools_examples.yaml
│       ├── unsupported_config.txt
│       ├── valid_config.json
│       └── valid_config.yaml
└── xtp-plugin-schema.json
```

# Files

--------------------------------------------------------------------------------
/examples/plugins/v1/gomodule/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/hash/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/maven/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/memory/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/myip/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/serper/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/sqlite/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/think/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/qr-code/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
// THIS FILE WAS GENERATED BY `xtp-rust-bindgen`. DO NOT EDIT.

#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/time/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
// THIS FILE WAS GENERATED BY `xtp-rust-bindgen`. DO NOT EDIT.

#![allow(non_snake_case)]
#![allow(unused_macros)]
use extism_pdk::*;

#[allow(unused)]
fn panic_if_key_missing() -> ! {
    panic!("missing key");
}

pub(crate) mod internal {
    pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
        let err = format!("{:?}", e);
        let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
        unsafe {
            extism_pdk::extism::error_set(mem.offset());
        }
        -1
    }
}

#[allow(unused)]
macro_rules! try_input {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(x) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

#[allow(unused)]
macro_rules! try_input_json {
    () => {{
        let x = extism_pdk::input();
        match x {
            Ok(extism_pdk::Json(x)) => x,
            Err(e) => return internal::return_error(e),
        }
    }};
}

use base64_serde::base64_serde_type;

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

mod exports {
    use super::*;

    #[unsafe(no_mangle)]
    pub extern "C" fn call() -> i32 {
        let ret =
            crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }

    #[unsafe(no_mangle)]
    pub extern "C" fn describe() -> i32 {
        let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));

        match ret {
            Ok(()) => 0,
            Err(e) => internal::return_error(e),
        }
    }
}

pub mod types {
    use super::*;

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct BlobResourceContents {
        /// A base64-encoded string representing the binary data of the item.
        #[serde(rename = "blob")]
        pub blob: String,

        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolRequest {
        #[serde(rename = "method")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub method: Option<String>,

        #[serde(rename = "params")]
        pub params: types::Params,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct CallToolResult {
        #[serde(rename = "content")]
        pub content: Vec<types::Content>,

        /// Whether the tool call ended in an error.
        ///
        /// If not set, this is assumed to be false (the call was successful).
        #[serde(rename = "isError")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub is_error: Option<bool>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Content {
        #[serde(rename = "annotations")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub annotations: Option<types::TextAnnotation>,

        /// The base64-encoded image data.
        #[serde(rename = "data")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub data: Option<String>,

        /// The MIME type of the image. Different providers may support different image types.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text content of the message.
        #[serde(rename = "text")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub text: Option<String>,

        #[serde(rename = "type")]
        pub r#type: types::ContentType,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum ContentType {
        #[default]
        #[serde(rename = "text")]
        Text,
        #[serde(rename = "image")]
        Image,
        #[serde(rename = "resource")]
        Resource,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ListToolsResult {
        /// The list of ToolDescription objects provided by this servlet.
        #[serde(rename = "tools")]
        pub tools: Vec<types::ToolDescription>,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct Params {
        #[serde(rename = "arguments")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub arguments: Option<serde_json::Map<String, serde_json::Value>>,

        #[serde(rename = "name")]
        pub name: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub enum Role {
        #[default]
        #[serde(rename = "assistant")]
        Assistant,
        #[serde(rename = "user")]
        User,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextAnnotation {
        /// Describes who the intended customer of this object or data is.
        ///
        /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
        #[serde(rename = "audience")]
        pub audience: Vec<types::Role>,

        /// Describes how important this data is for operating the server.
        ///
        /// A value of 1 means "most important," and indicates that the data is
        /// effectively required, while 0 means "least important," and indicates that
        /// the data is entirely optional.
        #[serde(rename = "priority")]
        pub priority: f32,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct TextResourceContents {
        /// The MIME type of this resource, if known.
        #[serde(rename = "mimeType")]
        #[serde(skip_serializing_if = "Option::is_none")]
        #[serde(default)]
        pub mime_type: Option<String>,

        /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
        #[serde(rename = "text")]
        pub text: String,

        /// The URI of this resource.
        #[serde(rename = "uri")]
        pub uri: String,
    }

    #[derive(
        Default,
        Debug,
        Clone,
        serde::Serialize,
        serde::Deserialize,
        extism_pdk::FromBytes,
        extism_pdk::ToBytes,
    )]
    #[encoding(Json)]
    pub struct ToolDescription {
        /// A description of the tool
        #[serde(rename = "description")]
        pub description: String,

        /// The JSON schema describing the argument input
        #[serde(rename = "inputSchema")]
        pub input_schema: serde_json::Map<String, serde_json::Value>,

        /// The name of the tool. It should match the plugin / binding name.
        #[serde(rename = "name")]
        pub name: String,
    }
}

mod raw_imports {
    use super::*;
    #[host_fn]
    extern "ExtismHost" {}
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/src/lib.rs:
--------------------------------------------------------------------------------

```rust
mod pdk;
mod qdrant_client;

use extism_pdk::*;
use pdk::types::{
    CallToolRequest, CallToolResult, Content, ContentType, ListToolsResult, ToolDescription,
};
use qdrant_client::*;
use serde_json::json;

fn get_qdrant_client() -> Result<QdrantClient, Error> {
    let qdrant_url = config::get("QDRANT_URL")?
        .ok_or_else(|| Error::msg("QDRANT_URL configuration is required but not set"))?;

    let mut client = QdrantClient::new_with_url(qdrant_url);

    // Check if API key is set in config
    if let Ok(Some(api_key)) = config::get("QDRANT_API_KEY") {
        client.set_api_key(api_key);
    }

    Ok(client)
}

fn ensure_collection_exists(
    client: &QdrantClient,
    collection_name: &str,
    vector_size: u32,
) -> Result<(), Error> {
    // check if the collection exists. If present, delete it.
    if let Ok(true) = client.collection_exists(collection_name) {
        println!("Collection `{}` exists", collection_name);
        match client.delete_collection(collection_name) {
            Ok(_) => println!("Collection `{}` deleted", collection_name),
            Err(e) => println!("Error deleting collection: {:?}", e),
        }
    };

    // Create collection
    let create_result = client.create_collection(collection_name, vector_size);
    println!("Create collection result is {:?}", create_result);

    Ok(())
}

pub(crate) fn call(input: CallToolRequest) -> Result<CallToolResult, Error> {
    match input.params.name.as_str() {
        "qdrant_store" => qdrant_store(input),
        "qdrant_find" => qdrant_find(input),
        "qdrant_create_collection" => qdrant_create_collection(input),
        _ => Ok(CallToolResult {
            is_error: Some(true),
            content: vec![Content {
                annotations: None,
                text: Some(format!("Unknown tool: {}", input.params.name)),
                mime_type: None,
                r#type: ContentType::Text,
                data: None,
            }],
        }),
    }
}

fn qdrant_store(input: CallToolRequest) -> Result<CallToolResult, Error> {
    let args = input.params.arguments.unwrap_or_default();

    let collection_name = args
        .get("collection_name")
        .and_then(|v| v.as_str())
        .ok_or_else(|| Error::msg("collection_name parameter is required"))?;

    let vector = args
        .get("vector")
        .and_then(|v| v.as_array())
        .ok_or_else(|| Error::msg("vector parameter is required"))?
        .iter()
        .map(|v| v.as_f64().unwrap_or_default())
        .collect::<Vec<f64>>();

    let text = args
        .get("text")
        .and_then(|v| v.as_str())
        .ok_or_else(|| Error::msg("text parameter is required"))?;

    let client = get_qdrant_client()?;
    ensure_collection_exists(&client, collection_name, vector.len() as u32)?;

    let point_id = uuid::Uuid::new_v4().to_string();
    let vector: Vec<f32> = vector.into_iter().map(|x| x as f32).collect();

    let mut points = Vec::new();
    points.push(Point {
        id: PointId::Uuid(point_id.clone()),
        vector,
        payload: json!({
            "text": text,
            "metadata": {},
        })
        .as_object()
        .map(|m| m.to_owned()),
    });

    client.upsert_points(collection_name, points)?;
    println!("Upsert points result is {:?}", ());

    Ok(CallToolResult {
        is_error: None,
        content: vec![Content {
            annotations: None,
            text: Some(format!(
                "Successfully stored document with ID: {}",
                point_id
            )),
            mime_type: None,
            r#type: ContentType::Text,
            data: None,
        }],
    })
}

fn qdrant_find(input: CallToolRequest) -> Result<CallToolResult, Error> {
    let args = input.params.arguments.unwrap_or_default();

    let collection_name = args
        .get("collection_name")
        .and_then(|v| v.as_str())
        .ok_or_else(|| Error::msg("collection_name parameter is required"))?;

    let vector = args
        .get("vector")
        .and_then(|v| v.as_array())
        .ok_or_else(|| Error::msg("vector parameter is required"))?
        .iter()
        .map(|v| v.as_f64().unwrap_or_default())
        .collect::<Vec<f64>>();

    let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(5);

    let client = get_qdrant_client()?;

    let vector_f32: Vec<f32> = vector.into_iter().map(|x| x as f32).collect();
    let search_result = client.search_points(collection_name, vector_f32, limit, None)?;

    let mut results = Vec::new();
    for point in search_result {
        if let Some(payload) = &point.payload {
            if let Some(text) = payload.get("text").and_then(|v| v.as_str()) {
                results.push(format!("Score: {:.4} - {}", point.score, text));
            }
        }
    }

    Ok(CallToolResult {
        is_error: None,
        content: vec![Content {
            annotations: None,
            text: Some(results.join("\n")),
            mime_type: None,
            r#type: ContentType::Text,
            data: None,
        }],
    })
}

fn qdrant_create_collection(input: CallToolRequest) -> Result<CallToolResult, Error> {
    let args = input.params.arguments.unwrap_or_default();

    let collection_name = args
        .get("collection_name")
        .and_then(|v| v.as_str())
        .ok_or_else(|| Error::msg("collection_name parameter is required"))?;

    let vector_size = args
        .get("vector_size")
        .and_then(|v| v.as_u64())
        .unwrap_or(384) as u32;

    let client = get_qdrant_client()?;
    ensure_collection_exists(&client, collection_name, vector_size)?;

    Ok(CallToolResult {
        is_error: None,
        content: vec![Content {
            annotations: None,
            text: Some(format!(
                "Successfully created collection '{}' with vector size {}",
                collection_name, vector_size
            )),
            mime_type: None,
            r#type: ContentType::Text,
            data: None,
        }],
    })
}

pub(crate) fn describe() -> Result<ListToolsResult, Error> {
    Ok(ListToolsResult {
        tools: vec![
            ToolDescription {
                name: "qdrant_create_collection".into(),
                description: "Creates a new collection in Qdrant with specified vector size".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "collection_name": {
                            "type": "string",
                            "description": "The name of the collection to create",
                        },
                        "vector_size": {
                            "type": "integer",
                            "description": "The size of vectors to be stored in this collection",
                            "default": 384
                        }
                    },
                    "required": ["collection_name"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "qdrant_store".into(),
                description: "Stores a document with its vector embedding in Qdrant.".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "collection_name": {
                            "type": "string",
                            "description": "The name of the collection to store the document in",
                        },
                        "text": {
                            "type": "string",
                            "description": "The text content to store",
                        },
                        "vector": {
                            "type": "array",
                            "items": {"type": "number"},
                            "description": "The vector embedding of the text.",
                        }
                    },
                    "required": ["collection_name", "text", "vector"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "qdrant_find".into(),
                description: "Finds similar documents in Qdrant using vector similarity search"
                    .into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "collection_name": {
                            "type": "string",
                            "description": "The name of the collection to search in",
                        },
                        "vector": {
                            "type": "array",
                            "items": {"type": "number"},
                            "description": "The query vector to search with.",
                        },
                        "limit": {
                            "type": "integer",
                            "description": "Maximum number of results to return",
                            "default": 5
                        }
                    },
                    "required": ["collection_name", "vector"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
        ],
    })
}

```

--------------------------------------------------------------------------------
/src/https_auth.rs:
--------------------------------------------------------------------------------

```rust
use crate::config::AuthConfig;
use reqwest::RequestBuilder;
use std::{cmp::Reverse, collections::HashMap};
use url::Url;

pub trait Authenticator {
    /// Adds authentication headers to the request if present in auths.
    fn add_auth(self, auths: &Option<HashMap<Url, AuthConfig>>, url: &Url) -> RequestBuilder;
}

impl Authenticator for RequestBuilder {
    fn add_auth(self, auths: &Option<HashMap<Url, AuthConfig>>, url: &Url) -> RequestBuilder {
        if let Some(auths) = auths {
            let mut auths: Vec<(&str, &AuthConfig)> =
                auths.iter().map(|(k, v)| (k.as_str(), v)).collect();
            auths.sort_by_key(|c| Reverse(c.0.len()));
            let url = url.to_string();
            for (k, v) in auths {
                if url.starts_with(k) {
                    return match v {
                        AuthConfig::Basic { username, password } => {
                            self.basic_auth(username, Some(password))
                        }
                        AuthConfig::Token { token } => self.bearer_auth(token),
                    };
                }
            }
        }

        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use reqwest::Client;
    use std::collections::HashMap;
    use url::Url;

    #[test]
    fn test_add_auth_basic_authentication() {
        let client = Client::new();
        let mut auths = HashMap::new();

        let url = Url::parse("https://api.example.com").unwrap();
        auths.insert(
            url.clone(),
            AuthConfig::Basic {
                username: "testuser".to_string(),
                password: "testpass".to_string(),
            },
        );

        let request = client.get("https://api.example.com/endpoint");
        let authenticated_request = request.add_auth(&Some(auths), &url);

        // We can't easily test the actual header since reqwest doesn't expose it,
        // but we can verify the method doesn't panic and returns a RequestBuilder
        // The fact that we got here without panicking means the method worked
        drop(authenticated_request);
    }

    #[test]
    fn test_add_auth_token_authentication() {
        let client = Client::new();
        let mut auths = HashMap::new();

        let url = Url::parse("https://api.example.com").unwrap();
        auths.insert(
            url.clone(),
            AuthConfig::Token {
                token: "bearer-token-123".to_string(),
            },
        );

        let request = client.get("https://api.example.com/endpoint");
        let authenticated_request = request.add_auth(&Some(auths), &url);

        // Verify the method completes without error
        // The fact that we got here without panicking means the method worked
        drop(authenticated_request);
    }

    #[test]
    fn test_add_auth_no_auths_provided() {
        let client = Client::new();
        let url = Url::parse("https://api.example.com").unwrap();

        let request = client.get("https://api.example.com/endpoint");
        let result_request = request.add_auth(&None, &url);

        // Should return the original request unchanged
        // The fact that we got here without panicking means the method worked
        drop(result_request);
    }

    #[test]
    fn test_add_auth_empty_auths_map() {
        let client = Client::new();
        let auths = HashMap::new();
        let url = Url::parse("https://api.example.com").unwrap();

        let request = client.get("https://api.example.com/endpoint");
        let result_request = request.add_auth(&Some(auths), &url);

        // Should return the original request unchanged when no matching auth
        // The fact that we got here without panicking means the method worked
        drop(result_request);
    }

    #[test]
    fn test_add_auth_url_prefix_matching() {
        let client = Client::new();
        let mut auths = HashMap::new();

        // Add auth for broader domain
        let domain_url = Url::parse("https://example.com").unwrap();
        auths.insert(
            domain_url,
            AuthConfig::Basic {
                username: "domain_user".to_string(),
                password: "domain_pass".to_string(),
            },
        );

        // Add auth for specific API endpoint (longer prefix)
        let api_url = Url::parse("https://example.com/api").unwrap();
        auths.insert(
            api_url,
            AuthConfig::Token {
                token: "api-token".to_string(),
            },
        );

        // Test that longer prefix wins
        let target_url = Url::parse("https://example.com/api/v1/data").unwrap();
        let request = client.get(target_url.as_str());
        let authenticated_request = request.add_auth(&Some(auths), &target_url);

        // The API token should be used (longest prefix)
        // The fact that we got here without panicking means the method worked
        drop(authenticated_request);
    }

    #[test]
    fn test_add_auth_url_no_match() {
        let client = Client::new();
        let mut auths = HashMap::new();

        let auth_url = Url::parse("https://api.example.com").unwrap();
        auths.insert(
            auth_url,
            AuthConfig::Basic {
                username: "testuser".to_string(),
                password: "testpass".to_string(),
            },
        );

        // Request to different domain
        let target_url = Url::parse("https://different.com/endpoint").unwrap();
        let request = client.get(target_url.as_str());
        let result_request = request.add_auth(&Some(auths), &target_url);

        // Should return the original request unchanged when no URL match
        // The fact that we got here without panicking means the method worked
        drop(result_request);
    }

    #[test]
    fn test_add_auth_multiple_auths_longest_prefix_wins() {
        let client = Client::new();
        let mut auths = HashMap::new();

        // Add multiple auths with different prefix lengths
        auths.insert(
            Url::parse("https://example.com").unwrap(),
            AuthConfig::Basic {
                username: "broad_user".to_string(),
                password: "broad_pass".to_string(),
            },
        );

        auths.insert(
            Url::parse("https://example.com/api").unwrap(),
            AuthConfig::Token {
                token: "api_token".to_string(),
            },
        );

        auths.insert(
            Url::parse("https://example.com/api/v1").unwrap(),
            AuthConfig::Basic {
                username: "v1_user".to_string(),
                password: "v1_pass".to_string(),
            },
        );

        // Test with URL that matches all three (longest should win)
        let target_url = Url::parse("https://example.com/api/v1/endpoint").unwrap();
        let request = client.get(target_url.as_str());
        let authenticated_request = request.add_auth(&Some(auths), &target_url);

        // Should use the v1 auth (longest prefix)
        // The fact that we got here without panicking means the method worked
        drop(authenticated_request);
    }

    #[test]
    fn test_add_auth_exact_url_match() {
        let client = Client::new();
        let mut auths = HashMap::new();

        let exact_url = Url::parse("https://api.example.com/v1/data").unwrap();
        auths.insert(
            exact_url.clone(),
            AuthConfig::Token {
                token: "exact-match-token".to_string(),
            },
        );

        let request = client.get(exact_url.as_str());
        let authenticated_request = request.add_auth(&Some(auths), &exact_url);

        // The fact that we got here without panicking means the method worked
        drop(authenticated_request);
    }

    #[test]
    fn test_add_auth_case_sensitive_urls() {
        let client = Client::new();
        let mut auths = HashMap::new();

        let auth_url = Url::parse("https://API.EXAMPLE.COM").unwrap();
        auths.insert(
            auth_url,
            AuthConfig::Basic {
                username: "testuser".to_string(),
                password: "testpass".to_string(),
            },
        );

        // Test with lowercase URL
        let target_url = Url::parse("https://api.example.com/endpoint").unwrap();
        let request = client.get(target_url.as_str());
        let result_request = request.add_auth(&Some(auths), &target_url);

        // Should not match due to case sensitivity
        // The fact that we got here without panicking means the method worked
        drop(result_request);
    }

    #[test]
    fn test_auth_config_types_comprehensive() {
        // Test all AuthConfig variants can be created and used
        let basic_auth = AuthConfig::Basic {
            username: "basic_user".to_string(),
            password: "basic_pass".to_string(),
        };

        let token_auth = AuthConfig::Token {
            token: "token_value".to_string(),
        };

        let client = Client::new();
        let url = Url::parse("https://test.com").unwrap();

        // Test both types can be used with add_auth
        let mut auths = HashMap::new();
        auths.insert(url.clone(), basic_auth);

        let request1 = client.get(url.as_str());
        let result1 = request1.add_auth(&Some(auths), &url);
        // The fact that we got here without panicking means the method worked
        drop(result1);

        let mut auths = HashMap::new();
        auths.insert(url.clone(), token_auth);

        let request2 = client.get(url.as_str());
        let result2 = request2.add_auth(&Some(auths), &url);
        // The fact that we got here without panicking means the method worked
        drop(result2);
    }
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/sqlite/src/lib.rs:
--------------------------------------------------------------------------------

```rust
mod pdk;

use extism_pdk::*;
use pdk::types::{
    CallToolRequest, CallToolResult, Content, ContentType, ListToolsResult, ToolDescription,
};
use rusqlite::Connection;
use serde_json::json;
use std::sync::Once;

static DB_INIT: Once = Once::new();

fn init_db(db_path: &str) -> Result<(), Error> {
    let _conn = Connection::open_with_flags(
        db_path,
        rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
    )?;

    Ok(())
}

fn get_db_path() -> Result<String, Error> {
    config::get("db_path")?
        .ok_or_else(|| Error::msg("db_path configuration is required but not set"))
}

fn execute_read_query(query: &str, db_path: &str) -> Result<String, Error> {
    let conn = Connection::open_with_flags(db_path, rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE)?;
    let mut stmt = conn.prepare(query)?;
    let column_names: Vec<String> = stmt.column_names().into_iter().map(String::from).collect();

    let rows = stmt.query_map([], |row| {
        let mut map = serde_json::Map::new();
        for (i, col_name) in column_names.iter().enumerate() {
            let value = match row.get_ref(i)? {
                rusqlite::types::ValueRef::Null => serde_json::Value::Null,
                rusqlite::types::ValueRef::Integer(i) => json!(i),
                rusqlite::types::ValueRef::Real(f) => json!(f),
                rusqlite::types::ValueRef::Text(s) => json!(s),
                rusqlite::types::ValueRef::Blob(b) => json!(b),
            };
            map.insert(col_name.clone(), value);
        }
        Ok(map)
    })?;

    let results: Vec<serde_json::Map<String, serde_json::Value>> =
        rows.filter_map(Result::ok).collect();
    Ok(serde_json::to_string(&results)?)
}

fn execute_write_query(query: &str, db_path: &str) -> Result<String, Error> {
    let conn = Connection::open_with_flags(db_path, rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE)?;
    let affected = conn.execute(query, [])?;
    Ok(json!({ "rows_affected": affected }).to_string())
}

fn create_table(query: &str, db_path: &str) -> Result<String, Error> {
    let conn = Connection::open_with_flags(db_path, rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE)?;
    conn.execute(query, [])?;
    Ok(json!({ "status": "success" }).to_string())
}

fn list_tables(db_path: &str) -> Result<String, Error> {
    let conn = Connection::open_with_flags(db_path, rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE)?;
    let mut stmt = conn.prepare("SELECT name FROM sqlite_master WHERE type='table'")?;
    let tables: Result<Vec<String>, _> = stmt.query_map([], |row| row.get(0))?.collect();
    Ok(json!({ "tables": tables? }).to_string())
}

fn describe_table(table_name: &str, db_path: &str) -> Result<String, Error> {
    let conn = Connection::open_with_flags(db_path, rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE)?;
    let mut stmt = conn.prepare(&format!("PRAGMA table_info({})", table_name))?;

    let columns = stmt.query_map([], |row| {
        Ok(json!({
            "cid": row.get::<_, i64>(0)?,
            "name": row.get::<_, String>(1)?,
            "type": row.get::<_, String>(2)?,
            "notnull": row.get::<_, bool>(3)?,
            "dflt_value": row.get::<_, Option<String>>(4)?,
            "pk": row.get::<_, bool>(5)?
        }))
    })?;

    let schema: Vec<serde_json::Value> = columns.filter_map(Result::ok).collect();
    Ok(json!({ "schema": schema }).to_string())
}

pub(crate) fn call(input: CallToolRequest) -> Result<CallToolResult, Error> {
    let db_path = get_db_path()?;
    DB_INIT.call_once(|| {
        init_db(&db_path).expect("Failed to initialize database");
    });

    match input.params.name.as_str() {
        "sqlite_read_query" => {
            let args = input.params.arguments.unwrap_or_default();
            let query = match args.get("query") {
                Some(v) if v.is_string() => v.as_str().unwrap(),
                _ => return Err(Error::msg("query parameter is required")),
            };

            let result = execute_read_query(query, &db_path)?;
            Ok(CallToolResult {
                is_error: None,
                content: vec![Content {
                    annotations: None,
                    text: Some(result),
                    mime_type: Some("application/json".to_string()),
                    r#type: ContentType::Text,
                    data: None,
                }],
            })
        }
        "sqlite_write_query" => {
            let args = input.params.arguments.unwrap_or_default();
            let query = match args.get("query") {
                Some(v) if v.is_string() => v.as_str().unwrap(),
                _ => return Err(Error::msg("query parameter is required")),
            };

            let result = execute_write_query(query, &db_path)?;
            Ok(CallToolResult {
                is_error: None,
                content: vec![Content {
                    annotations: None,
                    text: Some(result),
                    mime_type: Some("application/json".to_string()),
                    r#type: ContentType::Text,
                    data: None,
                }],
            })
        }
        "sqlite_create_table" => {
            let args = input.params.arguments.unwrap_or_default();
            let query = match args.get("query") {
                Some(v) if v.is_string() => v.as_str().unwrap(),
                _ => return Err(Error::msg("query parameter is required")),
            };

            let result = create_table(query, &db_path)?;
            Ok(CallToolResult {
                is_error: None,
                content: vec![Content {
                    annotations: None,
                    text: Some(result),
                    mime_type: Some("application/json".to_string()),
                    r#type: ContentType::Text,
                    data: None,
                }],
            })
        }
        "sqlite_list_tables" => {
            let result = list_tables(&db_path)?;
            Ok(CallToolResult {
                is_error: None,
                content: vec![Content {
                    annotations: None,
                    text: Some(result),
                    mime_type: Some("application/json".to_string()),
                    r#type: ContentType::Text,
                    data: None,
                }],
            })
        }
        "sqlite_describe_table" => {
            let args = input.params.arguments.unwrap_or_default();
            let table_name = match args.get("table_name") {
                Some(v) if v.is_string() => v.as_str().unwrap(),
                _ => return Err(Error::msg("table_name parameter is required")),
            };

            let result = describe_table(table_name, &db_path)?;
            Ok(CallToolResult {
                is_error: None,
                content: vec![Content {
                    annotations: None,
                    text: Some(result),
                    mime_type: Some("application/json".to_string()),
                    r#type: ContentType::Text,
                    data: None,
                }],
            })
        }
        _ => Ok(CallToolResult {
            is_error: Some(true),
            content: vec![Content {
                annotations: None,
                text: Some(format!("Unknown tool: {}", input.params.name)),
                mime_type: None,
                r#type: ContentType::Text,
                data: None,
            }],
        }),
    }
}

pub(crate) fn describe() -> Result<ListToolsResult, Error> {
    Ok(ListToolsResult {
        tools: vec![
            ToolDescription {
                name: "sqlite_read_query".into(),
                description: "Execute a SELECT query on the SQLite database".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "SELECT SQL query to execute",
                        }
                    },
                    "required": ["query"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "sqlite_write_query".into(),
                description: "Execute an INSERT, UPDATE, or DELETE query on the SQLite database"
                    .into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "SQL query to execute",
                        }
                    },
                    "required": ["query"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "sqlite_create_table".into(),
                description: "Create a new table in the SQLite database".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "CREATE TABLE SQL statement",
                        }
                    },
                    "required": ["query"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "sqlite_list_tables".into(),
                description: "List all tables in the SQLite database".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {},
                    "required": [],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "sqlite_describe_table".into(),
                description: "Get the schema information for a specific table".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "table_name": {
                            "type": "string",
                            "description": "Name of the table to describe",
                        }
                    },
                    "required": ["table_name"],
                })
                .as_object()
                .unwrap()
                .clone(),
            },
        ],
    })
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/github/branches.go:
--------------------------------------------------------------------------------

```go
package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/extism/go-pdk"
)

var (
	CreateBranchTool = ToolDescription{
		Name:        "gh-create-branch",
		Description: "Create a branch in a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":       prop("string", "The owner of the repository"),
				"repo":        prop("string", "The repository name"),
				"branch":      prop("string", "The branch name"),
				"from_branch": prop("string", "Source branch (defaults to `main` if not provided)"),
			},
			"required": []string{"owner", "repo", "branch", "from_branch"},
		},
	}
	ListPullRequestsTool = ToolDescription{
		Name:        "gh-list-pull-requests",
		Description: "Lists pull requests in a specified repository. Supports different response formats via accept parameter.",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":     prop("string", "The account owner of the repository. The name is not case sensitive."),
				"repo":      prop("string", "The name of the repository without the .git extension. The name is not case sensitive."),
				"state":     prop("string", "Either open, closed, or all to filter by state."),
				"head":      prop("string", "Filter pulls by head user or head organization and branch name in the format of user:ref-name or organization:ref-name."),
				"base":      prop("string", "Filter pulls by base branch name. Example: gh-pages"),
				"sort":      prop("string", "What to sort results by. Can be one of: created, updated, popularity, long-running"),
				"direction": prop("string", "The direction of the sort. Default: desc when sort is created or not specified, otherwise asc"),
				"per_page":  prop("integer", "The number of results per page (max 100)"),
				"page":      prop("integer", "The page number of the results to fetch"),
				"accept":    prop("string", "Response format: raw (default), text, html, or full. Raw returns body, text returns body_text, html returns body_html, full returns all."),
			},
			"required": []string{"owner", "repo"},
		},
	}
	CreatePullRequestTool = ToolDescription{
		Name:        "gh-create-pull-request",
		Description: "Create a pull request in a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":                 prop("string", "The owner of the repository"),
				"repo":                  prop("string", "The repository name"),
				"title":                 prop("string", "The title of the pull request"),
				"body":                  prop("string", "The body of the pull request"),
				"head":                  prop("string", "The branch you want to merge into the base branch"),
				"base":                  prop("string", "The branch you want to merge into"),
				"draft":                 prop("boolean", "Create as draft (optional)"),
				"maintainer_can_modify": prop("boolean", "Allow maintainers to modify the pull request"),
			},
			"required": []string{"owner", "repo", "title", "body", "head", "base"},
		},
	}
)

var BranchTools = []ToolDescription{
	CreateBranchTool,
	ListPullRequestsTool,
	CreatePullRequestTool,
}

type RefObjectSchema struct {
	Sha  string `json:"sha"`
	Type string `json:"type"`
	URL  string `json:"url"`
}
type RefSchema struct {
	Ref    string          `json:"ref"`
	NodeID string          `json:"node_id"`
	URL    string          `json:"url"`
	Object RefObjectSchema `json:"object"`
}

func branchCreate(apiKey, owner, repo, branch string, fromBranch *string) CallToolResult {
	from := "main"
	if fromBranch != nil {
		from = *fromBranch
	}
	sha, err := branchGetSha(apiKey, owner, repo, from)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to get sha for branch %s: %s", from, err)),
			}},
		}
	}

	url := fmt.Sprintf("https://api.github.com/repos/%s/%s/git/refs", owner, repo)
	req := pdk.NewHTTPRequest(pdk.MethodPost, url)
	req.SetHeader("Authorization", fmt.Sprintf("token %s", apiKey))
	req.SetHeader("Content-Type", "application/json")
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	data := map[string]interface{}{
		"ref": fmt.Sprintf("refs/heads/%s", branch),
		"sha": sha,
	}
	res, err := json.Marshal(data)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to marshal branch data: %s", err)),
			}},
		}
	}

	req.SetBody([]byte(res))
	resp := req.Send()
	if resp.Status() != 201 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to create branch: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}
}

type PullRequestSchema struct {
	Title               string `json:"title"`
	Body                string `json:"body"`
	Head                string `json:"head"`
	Base                string `json:"base"`
	Draft               bool   `json:"draft"`
	MaintainerCanModify bool   `json:"maintainer_can_modify"`
}

func branchPullRequestSchemaFromArgs(args map[string]interface{}) PullRequestSchema {
	prs := PullRequestSchema{
		Title: args["title"].(string),
		Body:  args["body"].(string),
		Head:  args["head"].(string),
		Base:  args["base"].(string),
	}
	if draft, ok := args["draft"].(bool); ok {
		prs.Draft = draft
	}
	if canModify, ok := args["maintainer_can_modify"].(bool); ok {
		prs.MaintainerCanModify = canModify
	}
	return prs
}

func pullRequestList(apiKey string, owner, repo string, args map[string]interface{}) (CallToolResult, error) {
	baseURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/pulls", owner, repo)
	params := make([]string, 0)

	// Handle state parameter
	if state, ok := args["state"].(string); ok && state != "" {
		switch state {
		case "open", "closed", "all":
			params = append(params, fmt.Sprintf("state=%s", state))
		}
	} else {
		params = append(params, "state=open") // Default value
	}

	// Handle head parameter (user:ref-name or organization:ref-name format)
	if head, ok := args["head"].(string); ok && head != "" {
		params = append(params, fmt.Sprintf("head=%s", head))
	}

	// Handle base parameter
	if base, ok := args["base"].(string); ok && base != "" {
		params = append(params, fmt.Sprintf("base=%s", base))
	}

	// Handle sort parameter
	sort := "created" // Default value
	if sortArg, ok := args["sort"].(string); ok && sortArg != "" {
		switch sortArg {
		case "created", "updated", "popularity", "long-running":
			sort = sortArg
		}
	}
	params = append(params, fmt.Sprintf("sort=%s", sort))

	// Handle direction parameter
	direction := "desc" // Default for created or unspecified sort
	if sort != "created" {
		direction = "asc" // Default for other sort types
	}
	if dirArg, ok := args["direction"].(string); ok {
		switch dirArg {
		case "asc", "desc":
			direction = dirArg
		}
	}
	params = append(params, fmt.Sprintf("direction=%s", direction))

	// Handle pagination
	perPage := 30 // Default value
	if perPageArg, ok := args["per_page"].(float64); ok {
		if perPageArg > 100 {
			perPage = 100 // Max value
		} else if perPageArg > 0 {
			perPage = int(perPageArg)
		}
	}
	params = append(params, fmt.Sprintf("per_page=%d", perPage))

	page := 1 // Default value
	if pageArg, ok := args["page"].(float64); ok && pageArg > 0 {
		page = int(pageArg)
	}
	params = append(params, fmt.Sprintf("page=%d", page))

	// Build final URL
	url := fmt.Sprintf("%s?%s", baseURL, strings.Join(params, "&"))
	pdk.Log(pdk.LogDebug, fmt.Sprint("Listing pull requests: ", url))

	// Make request
	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))

	// Handle Accept header based on requested format
	acceptHeader := "application/vnd.github+json" // Default recommended header
	if format, ok := args["accept"].(string); ok {
		switch format {
		case "raw":
			acceptHeader = "application/vnd.github.raw+json"
		case "text":
			acceptHeader = "application/vnd.github.text+json"
		case "html":
			acceptHeader = "application/vnd.github.html+json"
		case "full":
			acceptHeader = "application/vnd.github.full+json"
		}
	}
	req.SetHeader("Accept", acceptHeader)
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()

	// Handle response status codes
	switch resp.Status() {
	case 200:
		return CallToolResult{
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(string(resp.Body())),
			}},
		}, nil
	case 304:
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some("Not modified"),
			}},
		}, nil
	case 422:
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some("Validation failed, or the endpoint has been spammed."),
			}},
		}, nil
	default:
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Request failed with status %d: %s", resp.Status(), string(resp.Body()))),
			}},
		}, nil
	}
}

func branchCreatePullRequest(apiKey, owner, repo string, pr PullRequestSchema) CallToolResult {
	url := fmt.Sprintf("https://api.github.com/repos/%s/%s/pulls", owner, repo)
	req := pdk.NewHTTPRequest(pdk.MethodPost, url)
	req.SetHeader("Authorization", fmt.Sprintf("token %s", apiKey))
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")
	req.SetHeader("Content-Type", "application/json")

	res, err := json.Marshal(pr)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to marshal pull request data: %s", err)),
			}},
		}
	}

	req.SetBody([]byte(res))
	resp := req.Send()
	if resp.Status() != 201 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to create pull request: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}
}

func branchGetSha(apiKey, owner, repo, ref string) (string, error) {
	url := fmt.Sprintf("https://api.github.com/repos/%s/%s/git/refs/heads/%s", owner, repo, ref)
	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprintf("token %s", apiKey))
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()
	if resp.Status() != 200 {
		return "", fmt.Errorf("Failed to get main branch sha: %d", resp.Status())
	}

	var refDetail RefSchema
	json.Unmarshal(resp.Body(), &refDetail)
	return refDetail.Object.Sha, nil
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/github/issues.go:
--------------------------------------------------------------------------------

```go
package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/extism/go-pdk"
)

var (
	ListIssuesTool = ToolDescription{
		Name:        "gh-list-issues",
		Description: "List issues from a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":     prop("string", "The owner of the repository"),
				"repo":      prop("string", "The repository name"),
				"filter":    prop("string", "Filter by assigned, created, mentioned, subscribed, repos, all"),
				"state":     prop("string", "The state of the issues (open, closed, all)"),
				"labels":    prop("string", "A list of comma separated label names (e.g. bug,ui,@high)"),
				"sort":      prop("string", "Sort field (created, updated, comments)"),
				"direction": prop("string", "Sort direction (asc or desc)"),
				"since":     prop("string", "ISO 8601 timestamp (YYYY-MM-DDTHH:MM:SSZ)"),
				"collab":    prop("boolean", "Filter by issues that are collaborated on"),
				"orgs":      prop("boolean", "Filter by organization issues"),
				"owned":     prop("boolean", "Filter by owned issues"),
				"pulls":     prop("boolean", "Include pull requests in results"),
				"per_page":  prop("integer", "Number of results per page (max 100)"),
				"page":      prop("integer", "Page number for pagination"),
			},
			"required": []string{"owner", "repo"},
		},
	}
	CreateIssueTool = ToolDescription{
		Name:        "gh-create-issue",
		Description: "Create an issue on a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":     prop("string", "The owner of the repository"),
				"repo":      prop("string", "The repository name"),
				"title":     prop("string", "The title of the issue"),
				"body":      prop("string", "The body of the issue"),
				"state":     prop("string", "The state of the issue"),
				"assignees": arrprop("array", "The assignees of the issue", "string"),
				"milestone": prop("integer", "The milestone of the issue"),
			},
			"required": []string{"owner", "repo", "title", "body"},
		},
	}
	GetIssueTool = ToolDescription{
		Name:        "gh-get-issue",
		Description: "Get an issue from a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner": prop("string", "The owner of the repository"),
				"repo":  prop("string", "The repository name"),
				"issue": prop("integer", "The issue number"),
			},
			"required": []string{"owner", "repo", "issue"},
		},
	}
	AddIssueCommentTool = ToolDescription{
		Name:        "gh-add-issue-comment",
		Description: "Add a comment to an issue in a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner": prop("string", "The owner of the repository"),
				"repo":  prop("string", "The repository name"),
				"issue": prop("integer", "The issue number"),
				"body":  prop("string", "The body of the issue"),
			},
			"required": []string{"owner", "repo", "issue", "body"},
		},
	}
	UpdateIssueTool = ToolDescription{
		Name:        "gh-update-issue",
		Description: "Update an issue in a GitHub repository",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":     prop("string", "The owner of the repository"),
				"repo":      prop("string", "The repository name"),
				"issue":     prop("integer", "The issue number"),
				"title":     prop("string", "The title of the issue"),
				"body":      prop("string", "The body of the issue"),
				"state":     prop("string", "The state of the issue"),
				"assignees": arrprop("array", "The assignees of the issue", "string"),
				"milestone": prop("integer", "The milestone of the issue"),
			},
			"required": []string{"owner", "repo", "issue"},
		},
	}
	IssueTools = []ToolDescription{
		ListIssuesTool,
		CreateIssueTool,
		GetIssueTool,
		UpdateIssueTool,
		AddIssueCommentTool,
	}
)

type Issue struct {
	Title     string   `json:"title,omitempty"`
	Body      string   `json:"body,omitempty"`
	Assignees []string `json:"assignees,omitempty"`
	Milestone int      `json:"milestone,omitempty"`
	Labels    []string `json:"labels,omitempty"`
}

func issueList(apiKey string, owner, repo string, args map[string]interface{}) (CallToolResult, error) {
	baseURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/issues", owner, repo)
	params := make([]string, 0)

	// String parameters
	stringParams := map[string]string{
		"filter":    "assigned", // Default value
		"state":     "open",     // Default value
		"labels":    "",
		"sort":      "created", // Default value
		"direction": "desc",    // Default value
		"since":     "",
	}

	for key := range stringParams {
		if value, ok := args[key].(string); ok && value != "" {
			params = append(params, fmt.Sprintf("%s=%s", key, value))
		} else if stringParams[key] != "" {
			// Add default value if one exists
			params = append(params, fmt.Sprintf("%s=%s", key, stringParams[key]))
		}
	}

	// Boolean parameters
	boolParams := []string{"collab", "orgs", "owned", "pulls"}
	for _, param := range boolParams {
		if value, ok := args[param].(bool); ok {
			params = append(params, fmt.Sprintf("%s=%t", param, value))
		}
	}

	// Pagination parameters
	perPage := 30 // Default value
	if value, ok := args["per_page"].(float64); ok {
		if value > 100 {
			perPage = 100 // Max value
		} else if value > 0 {
			perPage = int(value)
		}
	}
	params = append(params, fmt.Sprintf("per_page=%d", perPage))

	page := 1 // Default value
	if value, ok := args["page"].(float64); ok && value > 0 {
		page = int(value)
	}
	params = append(params, fmt.Sprintf("page=%d", page))

	// Build final URL
	url := baseURL
	if len(params) > 0 {
		url = fmt.Sprintf("%s?%s", baseURL, strings.Join(params, "&"))
	}

	pdk.Log(pdk.LogDebug, fmt.Sprint("Listing issues: ", url))

	// Make request
	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to list issues: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}, nil
}

func issueFromArgs(args map[string]interface{}) Issue {
	data := Issue{}
	if title, ok := args["title"].(string); ok {
		data.Title = title
	}
	if body, ok := args["body"].(string); ok {
		data.Body = body
	}
	if assignees, ok := args["assignees"].([]interface{}); ok {
		for _, a := range assignees {
			data.Assignees = append(data.Assignees, a.(string))
		}
	}
	if milestone, ok := args["milestone"].(float64); ok {
		data.Milestone = int(milestone)
	}
	if labels, ok := args["labels"].([]interface{}); ok {
		for _, l := range labels {
			data.Labels = append(data.Labels, l.(string))
		}
	}
	return data
}

func issueCreate(apiKey string, owner, repo string, data Issue) (CallToolResult, error) {
	url := fmt.Sprint("https://api.github.com/repos/", owner, "/", repo, "/issues")
	pdk.Log(pdk.LogDebug, fmt.Sprint("Adding comment: ", url))

	req := pdk.NewHTTPRequest(pdk.MethodPost, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")
	req.SetHeader("Content-Type", "application/json")

	res, err := json.Marshal(data)

	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to create issue: ", err)),
			}},
		}, nil
	}

	req.SetBody([]byte(res))
	resp := req.Send()

	if resp.Status() != 201 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to create issue: ", resp.Status(), " ", string(resp.Body()))),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}, nil
}

func issueGet(apiKey string, owner, repo string, issue int) (CallToolResult, error) {
	url := fmt.Sprint("https://api.github.com/repos/", owner, "/", repo, "/issues/", issue)
	pdk.Log(pdk.LogDebug, fmt.Sprint("Getting issue: ", url))

	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")
	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to get issue: ", resp.Status())),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}, nil
}

func issueUpdate(apiKey string, owner, repo string, issue int, data Issue) (CallToolResult, error) {
	url := fmt.Sprint("https://api.github.com/repos/", owner, "/", repo, "/issues/", issue)
	pdk.Log(pdk.LogDebug, fmt.Sprint("Getting issue: ", url))

	req := pdk.NewHTTPRequest(pdk.MethodPatch, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")
	req.SetHeader("Content-Type", "application/json")

	res, err := json.Marshal(data)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to update issue: ", err)),
			}},
		}, nil
	}

	req.SetBody([]byte(res))
	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to update issue: ", resp.Status())),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}, nil
}

func issueAddComment(apiKey string, owner, repo string, issue int, comment string) (CallToolResult, error) {
	url := fmt.Sprint("https://api.github.com/repos/", owner, "/", repo, "/issues/", issue, "/comments")
	pdk.Log(pdk.LogDebug, fmt.Sprint("Adding comment: ", url))

	req := pdk.NewHTTPRequest(pdk.MethodPost, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github.v3+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")
	req.SetHeader("Content-Type", "application/json")

	res, err := json.Marshal(map[string]string{
		"body": comment,
	})

	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to create issue: ", err)),
			}},
		}, nil
	}

	req.SetBody([]byte(res))
	resp := req.Send()

	if resp.Status() != 201 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprint("Failed to add comment: ", resp.Status())),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}, nil
}

```

--------------------------------------------------------------------------------
/SKIP_TOOLS_GUIDE.md:
--------------------------------------------------------------------------------

```markdown
# Skip Tools Pattern Guide

This guide provides comprehensive documentation for using the `skip_tools` configuration in hyper-mcp, which allows you to filter out unwanted tools using powerful regex patterns.

## Overview

The `skip_tools` field in your plugin's `runtime_config` allows you to specify a list of regex patterns that will be used to exclude tools from being loaded at runtime. This is useful for:

- Removing debug tools in production environments
- Filtering out deprecated or experimental tools
- Excluding tools that conflict with your workflow
- Customizing the available tool set per environment

## How It Works

### Automatic Pattern Anchoring

All patterns in `skip_tools` are automatically anchored to match the entire tool name. This means:

```yaml
skip_tools:
  - "debug"  # Becomes "^debug$" - matches exactly "debug"
```

This prevents unintended partial matches. If you want to match parts of tool names, use explicit wildcards:

```yaml
skip_tools:
  - "debug.*"  # Matches "debug", "debugger", "debug_info", etc.
```

### Regex Compilation

All patterns are compiled into a single optimized `RegexSet` for efficient matching:
- O(1) lookup time regardless of pattern count
- Single compilation at startup
- Memory-efficient pattern storage

## Basic Patterns

### Exact Matches

Match specific tool names exactly:

```yaml
skip_tools:
  - "debug_tool"      # Matches only "debug_tool"
  - "test_runner"     # Matches only "test_runner"
  - "admin_panel"     # Matches only "admin_panel"
```

### Prefix Matching

Match tools that start with a specific string:

```yaml
skip_tools:
  - "debug.*"         # Matches "debug", "debugger", "debug_info"
  - "test_.*"         # Matches "test_unit", "test_integration", "test_e2e"
  - "dev_.*"          # Matches "dev_server", "dev_tools", "dev_helper"
```

### Suffix Matching

Match tools that end with a specific string:

```yaml
skip_tools:
  - ".*_test"         # Matches "unit_test", "integration_test", "load_test"
  - ".*_backup"       # Matches "data_backup", "config_backup", "db_backup"
  - ".*_deprecated"   # Matches "old_deprecated", "legacy_deprecated"
```

### Contains Matching

Match tools that contain a specific substring:

```yaml
skip_tools:
  - ".*debug.*"       # Matches "pre_debug_tool", "debug", "tool_debug_info"
  - ".*temp.*"        # Matches "temp_file", "cleanup_temp", "temp_storage_tool"
```

## Advanced Patterns

### Character Classes

Use character classes for flexible matching:

```yaml
skip_tools:
  - "tool_[0-9]+"           # Matches "tool_1", "tool_42", "tool_999"
  - "test_[a-z]+"           # Matches "test_unit", "test_api", "test_db"
  - "[A-Z][a-z]+Tool"       # Matches "DebugTool", "TestTool", "AdminTool"
  - "log_[0-9]{4}_[0-9]{2}" # Matches "log_2023_12", "log_2024_01"
```

### Alternation (OR Logic)

Match multiple alternatives:

```yaml
skip_tools:
  - "test_(unit|integration|e2e)"     # Matches "test_unit", "test_integration", "test_e2e"
  - "(debug|trace|log)_.*"            # Matches tools starting with "debug_", "trace_", or "log_"
  - ".*(temp|tmp|cache).*"            # Matches tools containing "temp", "tmp", or "cache"
  - "system_(admin|user|guest)_.*"    # Matches tools for different user types
```

### Quantifiers

Control how many characters or groups to match:

```yaml
skip_tools:
  - "tool_v[0-9]+"          # Matches "tool_v1", "tool_v10", "tool_v123"
  - "backup_[0-9]{8}"       # Matches exactly 8 digits: "backup_20240101"
  - "temp_[a-f0-9]{6,}"     # Matches 6+ hex chars: "temp_abc123", "temp_def456789"
  - "log_[0-9]{4}-[0-9]{2}" # Matches "log_2024-01", "log_2023-12"
```

### Negation with Character Classes

Skip tools that DON'T match certain patterns:

```yaml
skip_tools:
  - "[^a-z].*"              # Skip tools starting with non-lowercase letters
  - ".*[^0-9]$"             # Skip tools not ending with numbers
  - "tool_[^v].*"           # Skip tools starting with "tool_" but not "tool_v"
```

## Common Use Cases

### Environment-Specific Filtering

#### Development Environment
```yaml
skip_tools:
  - "prod_.*"               # Skip production tools
  - "deploy_.*"             # Skip deployment tools
  - "monitor_.*"            # Skip monitoring tools
```

#### Production Environment
```yaml
skip_tools:
  - "debug.*"               # Skip all debug tools
  - "test_.*"               # Skip all test tools
  - "dev_.*"                # Skip development tools
  - "mock_.*"               # Skip mock/stub tools
  - ".*_experimental"       # Skip experimental features
```

#### Testing Environment
```yaml
skip_tools:
  - "prod_.*"               # Skip production tools
  - "deploy_.*"             # Skip deployment tools
  - ".*_live"               # Skip live/production tools
```

### Tool Category Filtering

#### Skip Administrative Tools
```yaml
skip_tools:
  - "admin_.*"
  - "system_admin_.*"
  - "user_management_.*"
  - "permission_.*"
```

#### Skip Deprecated Tools
```yaml
skip_tools:
  - ".*_deprecated"
  - ".*_old"
  - "legacy_.*"
  - "v[0-9]_.*"             # Skip versioned legacy tools
```

#### Skip Resource-Heavy Tools
```yaml
skip_tools:
  - ".*_benchmark"
  - "load_test_.*"
  - "stress_.*"
  - "heavy_.*"
```

### Version-Based Filtering

```yaml
skip_tools:
  - ".*_v[0-9]"             # Skip v1, v2, etc. (keep latest)
  - ".*_beta"               # Skip beta tools
  - ".*_alpha"              # Skip alpha tools
  - "tool_[0-9]+\\.[0-9]+"  # Skip versioned tools like "tool_1.0"
```

## Special Character Escaping

When matching literal special characters, escape them with backslashes:

```yaml
skip_tools:
  - "file\\.exe"            # Matches "file.exe" literally
  - "script\\?"             # Matches "script?" literally
  - "temp\\*data"           # Matches "temp*data" literally
  - "path\\\\tool"          # Matches "path\tool" literally (double escape for backslash)
  - "price\\$calculator"    # Matches "price$calculator" literally
  - "regex\\[test\\]"       # Matches "regex[test]" literally
```

## Configuration Examples

### Simple Configuration
```yaml
plugins:
  my_plugin:
    url: "oci://registry.io/my-plugin:latest"
    runtime_config:
      skip_tools:
        - "debug_tool"
        - "test_runner"
```

### Comprehensive Configuration
```yaml
plugins:
  production_plugin:
    url: "oci://registry.io/prod-plugin:latest"
    runtime_config:
      skip_tools:
        # Exact matches
        - "debug_console"
        - "test_runner"

        # Pattern matches
        - "dev_.*"              # All dev tools
        - ".*_test"             # All test tools
        - "temp_.*"             # All temp tools
        - "mock_.*"             # All mock tools

        # Advanced patterns
        - "tool_v[0-9]"         # Versioned tools
        - "admin_(user|role)_.*" # Specific admin tools
        - "[0-9]+_backup"       # Numbered backups

      allowed_hosts: ["api.example.com"]
      memory_limit: "512Mi"
```

### Multi-Environment Setup
```yaml
# config.dev.yaml
plugins:
  app_plugin:
    url: "oci://registry.io/app-plugin:dev"
    runtime_config:
      skip_tools:
        - "prod_.*"
        - "deploy_.*"

---
# config.prod.yaml
plugins:
  app_plugin:
    url: "oci://registry.io/app-plugin:latest"
    runtime_config:
      skip_tools:
        - "debug.*"
        - "test_.*"
        - "dev_.*"
        - ".*_experimental"
```

## Best Practices

### 1. Start Simple, Then Refine
```yaml
# Start with broad patterns
skip_tools:
  - "debug.*"
  - "test_.*"

# Refine to be more specific as needed
skip_tools:
  - "debug_(console|panel)"  # Only skip specific debug tools
  - "test_(unit|integration)" # Only skip specific test types
```

### 2. Use Comments for Complex Patterns
```yaml
skip_tools:
  - "tool_[0-9]+"             # Skip numbered tools (tool_1, tool_2, etc.)
  - ".*_(alpha|beta|rc[0-9]+)" # Skip pre-release versions
  - "temp_[0-9]{8}_.*"        # Skip dated temporary tools
```

### 3. Group Related Patterns
```yaml
skip_tools:
  # Debug and development tools
  - "debug.*"
  - "dev_.*"
  - ".*_dev"

  # Testing tools
  - "test_.*"
  - ".*_test"
  - "mock_.*"

  # Administrative tools
  - "admin_.*"
  - "system_.*"
```

### 4. Consider Performance
```yaml
# Good: Specific patterns
skip_tools:
  - "debug_tool"
  - "test_runner"

# Less optimal: Overly broad patterns that might match many tools
skip_tools:
  - ".*"  # This would skip everything - not useful
```

## Troubleshooting

### Pattern Not Working?

1. **Check anchoring**: Remember patterns are auto-anchored
   ```yaml
   # This matches only "debug" exactly
   - "debug"

   # This matches "debug", "debugger", "debug_tool", etc.
   - "debug.*"
   ```

2. **Escape special characters**:
   ```yaml
   # Wrong: Will treat . as wildcard
   - "file.exe"

   # Correct: Escapes the literal dot
   - "file\\.exe"
   ```

3. **Test your patterns**: Use a regex tester to validate complex patterns

### Debugging Skip Rules

Enable debug logging to see which tools are being skipped:

```bash
RUST_LOG=debug hyper-mcp --config config.yaml
```

## Migration from Old Format

If you were using simple string arrays before:

```yaml
# Old format (if it existed)
skip_tools: ["debug_tool", "test_runner"]

# New format (same result, but now with regex support)
skip_tools: ["debug_tool", "test_runner"]

# New format with patterns (more powerful)
skip_tools: ["debug.*", "test_.*"]
```

## Error Handling

### Invalid Regex Patterns

If you provide an invalid regex pattern, configuration loading will fail:

```yaml
# This will cause an error - unclosed bracket
skip_tools:
  - "tool_[invalid"
```

Error message will indicate the problematic pattern and suggest corrections.

### Empty Patterns

These configurations are all valid:

```yaml
# No skip_tools field - no tools skipped
runtime_config:
  allowed_hosts: ["*"]

# Empty array - no tools skipped
runtime_config:
  skip_tools: []

# Null value - no tools skipped
runtime_config:
  skip_tools: null
```

## Performance Characteristics

- **Startup**: O(n) pattern compilation where n = number of patterns
- **Runtime**: O(1) tool name checking regardless of pattern count
- **Memory**: Minimal overhead, patterns compiled into efficient state machine
- **Scalability**: Handles hundreds of patterns efficiently

## Advanced Topics

### Complex Business Logic

```yaml
skip_tools:
  # Skip tools for specific environments
  - "prod_(?!api_).*"         # Skip prod tools except prod_api_*
  - "test_(?!smoke_).*"       # Skip test tools except smoke tests

  # Skip based on naming conventions
  - "[A-Z]{2,}_.*"            # Skip tools starting with 2+ capitals
  - ".*_[0-9]{4}[0-9]{2}[0-9]{2}" # Skip daily-dated tools
```

### Integration with External Tools

You can generate `skip_tools` patterns dynamically:

```bash
# Generate patterns from external source
echo "skip_tools:" > config.yaml
external-tool --list-deprecated | sed 's/^/  - "/' | sed 's/$/\"/' >> config.yaml
```

### Conditional Configuration

Use different configs for different scenarios:

```yaml
# Base configuration
base_skip_patterns: &base_skip
  - "debug.*"
  - "test_.*"

# Environment-specific additions
prod_additional: &prod_additional
  - "dev_.*"
  - ".*_experimental"

plugins:
  my_plugin:
    runtime_config:
      skip_tools:
        - *base_skip
        - *prod_additional  # YAML doesn't support this directly,
                            # but you can use templating tools
```

This guide should help you make full use of the powerful `skip_tools` pattern matching capabilities in hyper-mcp!

```

--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/src/lib.rs:
--------------------------------------------------------------------------------

```rust
mod embedded;
mod pdk;

use ab_glyph::{Font, FontArc, PxScale, ScaleFont};
use base64::Engine;
use extism_pdk::*;
use image::Rgba;
use imageproc::drawing::draw_text_mut;
use pdk::types::{
    CallToolRequest, CallToolResult, Content, ContentType, ListToolsResult, ToolDescription,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::io::Cursor;

#[derive(Debug, Serialize, Deserialize)]
struct Example {
    text: Vec<String>,
    url: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct MemeTemplate {
    id: String,
    name: String,
    lines: u32,
    overlays: u32,
    styles: Vec<String>,
    blank: String,
    example: Example,
    source: Option<String>,
    keywords: Vec<String>,
    #[serde(rename = "_self")]
    self_link: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct TemplateConfig {
    name: String,
    source: String,
    keywords: Vec<String>,
    text: Vec<TextConfig>,
    example: Vec<String>,
    overlay: Vec<OverlayConfig>,
}

#[derive(Debug, Serialize, Deserialize)]
struct TextConfig {
    style: String,
    color: String,
    font: String,
    anchor_x: f32,
    anchor_y: f32,
    angle: f32,
    scale_x: f32,
    scale_y: f32,
    align: String,
    start: f32,
    stop: f32,
}

#[derive(Debug, Serialize, Deserialize)]
struct OverlayConfig {
    center_x: f32,
    center_y: f32,
    angle: f32,
    scale: f32,
}

pub(crate) fn call(input: CallToolRequest) -> Result<CallToolResult, Error> {
    match input.params.name.as_str() {
        "meme_list_templates" => list_templates(input),
        "meme_get_template" => get_template(input),
        "meme_generate" => generate_meme(input),
        _ => Ok(CallToolResult {
            is_error: Some(true),
            content: vec![Content {
                annotations: None,
                text: Some(format!("Unknown tool: {}", input.params.name)),
                mime_type: None,
                r#type: ContentType::Text,
                data: None,
            }],
        }),
    }
}

fn list_templates(_input: CallToolRequest) -> Result<CallToolResult, Error> {
    let templates_json = embedded::TEMPLATES_JSON;
    let templates: Vec<MemeTemplate> = serde_json::from_str(templates_json)?;

    Ok(CallToolResult {
        is_error: None,
        content: vec![Content {
            annotations: None,
            text: Some(serde_json::to_string_pretty(&templates)?),
            mime_type: Some("application/json".to_string()),
            r#type: ContentType::Text,
            data: None,
        }],
    })
}

fn get_template(input: CallToolRequest) -> Result<CallToolResult, Error> {
    let args = input.params.arguments.unwrap_or_default();
    let template_id = args
        .get("template_id")
        .and_then(|v| v.as_str())
        .ok_or_else(|| Error::msg("template_id is required"))?;

    let templates: Vec<MemeTemplate> = serde_json::from_str(embedded::TEMPLATES_JSON)?;

    let template = templates
        .iter()
        .find(|t| t.id == template_id)
        .ok_or_else(|| Error::msg("Template not found"))?;

    Ok(CallToolResult {
        is_error: None,
        content: vec![Content {
            annotations: None,
            text: Some(serde_json::to_string_pretty(&template)?),
            mime_type: Some("application/json".to_string()),
            r#type: ContentType::Text,
            data: None,
        }],
    })
}

fn generate_meme(input: CallToolRequest) -> Result<CallToolResult, Error> {
    let args = input.params.arguments.unwrap_or_default();

    let template_id = args
        .get("template_id")
        .and_then(|v| v.as_str())
        .ok_or_else(|| Error::msg("template_id is required"))?;

    let texts = args
        .get("texts")
        .and_then(|v| v.as_array())
        .ok_or_else(|| Error::msg("texts array is required"))?;

    // Load template configuration
    let config = TemplateConfig::load(template_id)?;

    // Get the default image from embedded resources
    let image_name = if embedded::get_template_image(template_id, "default.jpg").is_some() {
        "default.jpg"
    } else if embedded::get_template_image(template_id, "default.png").is_some() {
        "default.png"
    } else {
        return Err(Error::msg(format!(
            "No default template image found for {}",
            template_id
        )));
    };

    let image_data = embedded::get_template_image(template_id, image_name).ok_or_else(|| {
        Error::msg(format!(
            "Template image {} {} not found",
            template_id, image_name
        ))
    })?;

    let mut image = image::load_from_memory(image_data)?.to_rgba8();
    let (image_width, image_height) = image.dimensions();

    let font = FontArc::try_from_slice(embedded::FONT_DATA)?;

    // Draw each text configuration
    for (i, text_config) in config.text.iter().enumerate() {
        if i >= texts.len() {
            break;
        }

        let text = texts[i]
            .as_str()
            .ok_or_else(|| Error::msg("Invalid text entry"))?;

        let text = if text_config.style == "upper" {
            text.to_uppercase()
        } else {
            text.to_string()
        };

        // Calculate initial desired height based on image dimensions and config scale
        let desired_height = (image_height as f32 * text_config.scale_y).max(1.0);

        // Calculate maximum available width based on alignment
        let padding = image_width as f32 * 0.05; // 5% padding on each side
        let available_width = match text_config.align.as_str() {
            "center" => image_width as f32 - (2.0 * padding),
            "left" => {
                image_width as f32 - (image_width as f32 * text_config.anchor_x) - (2.0 * padding)
            }
            "right" => (image_width as f32 * text_config.anchor_x) - (2.0 * padding),
            _ => image_width as f32 - (2.0 * padding),
        };

        // Calculate appropriate scale that prevents overflow
        let scale = calculate_max_scale(&font, &text, available_width, desired_height);

        // Calculate text width for positioning using the adjusted scale
        let text_width = calculate_text_width(&font, &text, scale);

        // Calculate x position based on anchor and alignment, now with padding
        let x = match text_config.align.as_str() {
            "center" => ((image_width as f32 - text_width) / 2.0
                + (image_width as f32 * text_config.anchor_x))
                .max(padding) as i32,
            "left" => ((image_width as f32 * text_config.anchor_x) + padding) as i32,
            "right" => ((image_width as f32 * text_config.anchor_x) - text_width - padding)
                .max(padding) as i32,
            _ => ((image_width as f32 - text_width) / 2.0).max(padding) as i32,
        };

        // Calculate y position based on anchor
        let y = (image_height as f32 * text_config.anchor_y) as i32;

        // Convert color string to RGBA
        let color = color_to_rgba(&text_config.color);

        draw_text_mut(&mut image, color, x, y, scale, &font, &text);
    }

    // Convert image to bytes
    let mut output_bytes = Vec::new();
    let dynamic_image = image::DynamicImage::ImageRgba8(image);
    dynamic_image.write_to(&mut Cursor::new(&mut output_bytes), image::ImageFormat::Png)?;

    Ok(CallToolResult {
        is_error: None,
        content: vec![Content {
            annotations: None,
            text: None,
            mime_type: Some("image/png".to_string()),
            r#type: ContentType::Image,
            data: Some(base64::engine::general_purpose::STANDARD.encode(&output_bytes)),
        }],
    })
}

fn calculate_text_width(font: &FontArc, text: &str, scale: PxScale) -> f32 {
    let scaled_font = font.as_scaled(scale);
    let mut width = 0.0;

    for c in text.chars() {
        let id = scaled_font.glyph_id(c);
        width += scaled_font.h_advance(id);

        if let Some(next_char) = text.chars().nth(1) {
            let next_id = scaled_font.glyph_id(next_char);
            width += scaled_font.kern(id, next_id);
        }
    }

    width
}

fn color_to_rgba(color: &str) -> Rgba<u8> {
    match color.to_lowercase().as_str() {
        "white" => Rgba([255, 255, 255, 255]),
        "black" => Rgba([0, 0, 0, 255]),
        "red" => Rgba([255, 0, 0, 255]),
        "green" => Rgba([0, 255, 0, 255]),
        "blue" => Rgba([0, 0, 255, 255]),
        _ => Rgba([255, 255, 255, 255]), // Fallback to white
    }
}

fn calculate_max_scale(
    font: &FontArc,
    text: &str,
    target_width: f32,
    desired_height: f32,
) -> PxScale {
    let initial_scale = PxScale::from(desired_height);
    let initial_width = calculate_text_width(font, text, initial_scale);

    if initial_width <= target_width {
        return initial_scale;
    }

    // Scale down proportionally if text is too wide
    let scale_factor = target_width / initial_width;
    PxScale::from(desired_height * scale_factor)
}

impl TemplateConfig {
    fn load(template_id: &str) -> Result<Self, Error> {
        let config_contents = embedded::get_template_config(template_id)
            .ok_or_else(|| Error::msg(format!("Template {} not found", template_id)))?;

        let config: TemplateConfig = serde_yaml::from_str(config_contents)
            .map_err(|e| Error::msg(format!("Failed to parse config: {}", e)))?;
        Ok(config)
    }
}

pub(crate) fn describe() -> Result<ListToolsResult, Error> {
    Ok(ListToolsResult {
        tools: vec![
            ToolDescription {
                name: "meme_list_templates".into(),
                description: "Lists all available meme templates".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {},
                    "required": []
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "meme_get_template".into(),
                description: "Get details about a specific meme template".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "template_id": {
                            "type": "string",
                            "description": "The ID of the template to retrieve",
                        }
                    },
                    "required": ["template_id"]
                })
                .as_object()
                .unwrap()
                .clone(),
            },
            ToolDescription {
                name: "meme_generate".into(),
                description: "Generate a meme using a template and custom text".into(),
                input_schema: json!({
                    "type": "object",
                    "properties": {
                        "template_id": {
                            "type": "string",
                            "description": "The ID of the template to use",
                        },
                        "texts": {
                            "type": "array",
                            "items": {
                                "type": "string"
                            },
                            "description": "Array of text strings to place on the meme",
                        }
                    },
                    "required": ["template_id", "texts"]
                })
                .as_object()
                .unwrap()
                .clone(),
            },
        ],
    })
}

```

--------------------------------------------------------------------------------
/examples/plugins/v1/github/repo.go:
--------------------------------------------------------------------------------

```go
package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/extism/go-pdk"
)

var (
	GetRepositoryContributorsTool = ToolDescription{
		Name:        "gh-get-repo-contributors",
		Description: "Get the list of contributors for a GitHub repository, including their contributions count and profile details",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":    prop("string", "The owner of the repository"),
				"repo":     prop("string", "The repository name"),
				"per_page": prop("integer", "Number of results per page (max 100)"),
				"page":     prop("integer", "Page number for pagination"),
			},
			"required": []string{"owner", "repo"},
		},
	}
	GetRepositoryCollaboratorsTool = ToolDescription{
		Name:        "gh-get-repo-collaborators",
		Description: "Get the list of collaborators for a GitHub repository, including their permissions and profile details",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner":    prop("string", "The owner of the repository"),
				"repo":     prop("string", "The repository name"),
				"per_page": prop("integer", "Number of results per page (max 100)"),
				"page":     prop("integer", "Page number for pagination"),
			},
			"required": []string{"owner", "repo"},
		},
	}
	GetRepositoryDetailsTool = ToolDescription{
		Name:        "gh-get-repo-details",
		Description: "Get detailed information about a GitHub repository, including stars, forks, issues, and more",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"owner": prop("string", "The owner of the repository"),
				"repo":  prop("string", "The repository name"),
			},
			"required": []string{"owner", "repo"},
		},
	}
	ListReposTool = ToolDescription{
		Name:        "gh-list-repos",
		Description: "List repositories for a GitHub user or organization",
		InputSchema: schema{
			"type": "object",
			"properties": props{
				"username":  prop("string", "The GitHub username or organization name"),
				"type":      prop("string", "The type of repositories to list (all, owner, member)"),
				"sort":      prop("string", "The sort field (created, updated, pushed, full_name)"),
				"direction": prop("string", "The sort direction (asc or desc)"),
				"per_page":  prop("integer", "Number of results per page (max 100)"),
				"page":      prop("integer", "Page number for pagination"),
			},
			"required": []string{"username"},
		},
	}
	RepoTools = []ToolDescription{
		GetRepositoryContributorsTool,
		GetRepositoryCollaboratorsTool,
		GetRepositoryDetailsTool,
		ListReposTool,
	}
)

type Contributor struct {
	Login             string `json:"login"`
	ID                int    `json:"id"`
	NodeID            string `json:"node_id"`
	AvatarURL         string `json:"avatar_url"`
	GravatarID        string `json:"gravatar_id"`
	URL               string `json:"url"`
	HTMLURL           string `json:"html_url"`
	FollowersURL      string `json:"followers_url"`
	FollowingURL      string `json:"following_url"`
	GistsURL          string `json:"gists_url"`
	StarredURL        string `json:"starred_url"`
	SubscriptionsURL  string `json:"subscriptions_url"`
	OrganizationsURL  string `json:"organizations_url"`
	ReposURL          string `json:"repos_url"`
	EventsURL         string `json:"events_url"`
	ReceivedEventsURL string `json:"received_events_url"`
	Type              string `json:"type"`
	SiteAdmin         bool   `json:"site_admin"`
	Contributions     int    `json:"contributions"`
}

func reposGetContributors(apiKey string, owner, repo string, args map[string]interface{}) (CallToolResult, error) {
	baseURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/contributors", owner, repo)
	params := make([]string, 0)

	// Pagination parameters
	perPage := 30 // Default value
	if value, ok := args["per_page"].(float64); ok {
		if value > 100 {
			perPage = 100 // Max value
		} else if value > 0 {
			perPage = int(value)
		}
	}
	params = append(params, fmt.Sprintf("per_page=%d", perPage))

	page := 1 // Default value
	if value, ok := args["page"].(float64); ok && value > 0 {
		page = int(value)
	}
	params = append(params, fmt.Sprintf("page=%d", page))

	// Build final URL
	url := baseURL
	if len(params) > 0 {
		url = fmt.Sprintf("%s?%s", baseURL, strings.Join(params, "&"))
	}

	pdk.Log(pdk.LogDebug, fmt.Sprint("Fetching contributors: ", url))

	// Make request
	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to fetch contributors: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}, nil
	}

	// Parse the response
	var contributors []Contributor
	if err := json.Unmarshal(resp.Body(), &contributors); err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to parse contributors: %s", err)),
			}},
		}, nil
	}

	// Marshal the response
	responseJSON, err := json.Marshal(contributors)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to marshal response: %s", err)),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(responseJSON)),
		}},
	}, nil
}

type Collaborator struct {
	Login             string `json:"login"`
	ID                int    `json:"id"`
	NodeID            string `json:"node_id"`
	AvatarURL         string `json:"avatar_url"`
	GravatarID        string `json:"gravatar_id"`
	URL               string `json:"url"`
	HTMLURL           string `json:"html_url"`
	FollowersURL      string `json:"followers_url"`
	FollowingURL      string `json:"following_url"`
	GistsURL          string `json:"gists_url"`
	StarredURL        string `json:"starred_url"`
	SubscriptionsURL  string `json:"subscriptions_url"`
	OrganizationsURL  string `json:"organizations_url"`
	ReposURL          string `json:"repos_url"`
	EventsURL         string `json:"events_url"`
	ReceivedEventsURL string `json:"received_events_url"`
	Type              string `json:"type"`
	SiteAdmin         bool   `json:"site_admin"`
	Permissions       struct {
		Admin bool `json:"admin"`
		Push  bool `json:"push"`
		Pull  bool `json:"pull"`
	} `json:"permissions"`
}

func reposGetCollaborators(apiKey string, owner, repo string, args map[string]interface{}) (CallToolResult, error) {
	baseURL := fmt.Sprintf("https://api.github.com/repos/%s/%s/collaborators", owner, repo)
	params := make([]string, 0)

	// Pagination parameters
	perPage := 30 // Default value
	if value, ok := args["per_page"].(float64); ok {
		if value > 100 {
			perPage = 100 // Max value
		} else if value > 0 {
			perPage = int(value)
		}
	}
	params = append(params, fmt.Sprintf("per_page=%d", perPage))

	page := 1 // Default value
	if value, ok := args["page"].(float64); ok && value > 0 {
		page = int(value)
	}
	params = append(params, fmt.Sprintf("page=%d", page))

	// Build final URL
	url := baseURL
	if len(params) > 0 {
		url = fmt.Sprintf("%s?%s", baseURL, strings.Join(params, "&"))
	}

	pdk.Log(pdk.LogDebug, fmt.Sprint("Fetching collaborators: ", url))

	// Make request
	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to fetch collaborators: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}, nil
	}

	// Parse the response
	var collaborators []Collaborator
	if err := json.Unmarshal(resp.Body(), &collaborators); err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to parse collaborators: %s", err)),
			}},
		}, nil
	}

	// Marshal the response
	responseJSON, err := json.Marshal(collaborators)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to marshal response: %s", err)),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(responseJSON)),
		}},
	}, nil
}

type RepositoryDetails struct {
	Name        string `json:"name"`
	FullName    string `json:"full_name"`
	Description string `json:"description"`
	Private     bool   `json:"private"`
	Owner       struct {
		Login string `json:"login"`
	} `json:"owner"`
	HTMLURL       string `json:"html_url"`
	Stargazers    int    `json:"stargazers_count"`
	Watchers      int    `json:"watchers_count"`
	Forks         int    `json:"forks_count"`
	OpenIssues    int    `json:"open_issues_count"`
	DefaultBranch string `json:"default_branch"`
	CreatedAt     string `json:"created_at"`
	UpdatedAt     string `json:"updated_at"`
	PushedAt      string `json:"pushed_at"`
}

func reposGetDetails(apiKey string, owner, repo string) (CallToolResult, error) {
	url := fmt.Sprintf("https://api.github.com/repos/%s/%s", owner, repo)
	pdk.Log(pdk.LogDebug, fmt.Sprint("Fetching repository details: ", url))

	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to fetch repository details: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}, nil
	}

	var repoDetails RepositoryDetails
	if err := json.Unmarshal(resp.Body(), &repoDetails); err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to parse repository details: %s", err)),
			}},
		}, nil
	}

	responseJSON, err := json.Marshal(repoDetails)
	if err != nil {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to marshal response: %s", err)),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(responseJSON)),
		}},
	}, nil
}

func reposList(apiKey string, username string, args map[string]interface{}) (CallToolResult, error) {
	baseURL := fmt.Sprintf("https://api.github.com/users/%s/repos", username)
	params := make([]string, 0)

	// Optional parameters
	if value, ok := args["type"].(string); ok && value != "" {
		params = append(params, fmt.Sprintf("type=%s", value))
	}
	if value, ok := args["sort"].(string); ok && value != "" {
		params = append(params, fmt.Sprintf("sort=%s", value))
	}
	if value, ok := args["direction"].(string); ok && value != "" {
		params = append(params, fmt.Sprintf("direction=%s", value))
	}

	// Pagination parameters
	perPage := 30 // Default value
	if value, ok := args["per_page"].(float64); ok {
		if value > 100 {
			perPage = 100 // Max value
		} else if value > 0 {
			perPage = int(value)
		}
	}
	params = append(params, fmt.Sprintf("per_page=%d", perPage))

	page := 1 // Default value
	if value, ok := args["page"].(float64); ok && value > 0 {
		page = int(value)
	}
	params = append(params, fmt.Sprintf("page=%d", page))

	// Build final URL
	url := baseURL
	if len(params) > 0 {
		url = fmt.Sprintf("%s?%s", baseURL, strings.Join(params, "&"))
	}

	pdk.Log(pdk.LogDebug, fmt.Sprint("Fetching repositories: ", url))

	// Make request
	req := pdk.NewHTTPRequest(pdk.MethodGet, url)
	req.SetHeader("Authorization", fmt.Sprint("token ", apiKey))
	req.SetHeader("Accept", "application/vnd.github+json")
	req.SetHeader("User-Agent", "github-mcpx-servlet")

	resp := req.Send()
	if resp.Status() != 200 {
		return CallToolResult{
			IsError: some(true),
			Content: []Content{{
				Type: ContentTypeText,
				Text: some(fmt.Sprintf("Failed to fetch repositories: %d %s", resp.Status(), string(resp.Body()))),
			}},
		}, nil
	}

	return CallToolResult{
		Content: []Content{{
			Type: ContentTypeText,
			Text: some(string(resp.Body())),
		}},
	}, nil
}

```
Page 3/8FirstPrevNextLast