#
tokens: 49935/50000 17/231 files (page 3/11)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 3 of 11. Use http://codebase.md/tuananh/hyper-mcp?lines=true&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

--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: Release
  2 | 
  3 | on:
  4 |   push:
  5 |     tags:
  6 |       - "v*" # Match tags like v1.0.0, v2.1.3 etc.
  7 | 
  8 | env:
  9 |   CARGO_TERM_COLOR: always
 10 |   REGISTRY: ghcr.io
 11 | 
 12 | jobs:
 13 |   build-oci-images:
 14 |     strategy:
 15 |       matrix:
 16 |         include:
 17 |           - os: ubuntu-24.04 # For amd64, consistent with nightly
 18 |             arch: amd64
 19 |           - os: ubuntu-24.04-arm # For arm64
 20 |             arch: arm64
 21 |     runs-on: ${{ matrix.os }}
 22 |     permissions:
 23 |       contents: write
 24 |       packages: write
 25 |       id-token: write # needed for keyless signing
 26 | 
 27 |     steps:
 28 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
 29 |         with:
 30 |           fetch-depth: 0
 31 |           submodules: true
 32 |       - name: Set up Docker Buildx
 33 |         uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
 34 | 
 35 |       - name: Install cosign
 36 |         uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
 37 | 
 38 |       - name: Log in to GitHub Container Registry
 39 |         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
 40 |         with:
 41 |           registry: ${{ env.REGISTRY }}
 42 |           username: ${{ github.actor }}
 43 |           password: ${{ secrets.GITHUB_TOKEN }}
 44 | 
 45 |       # tag images with both git tag & latest
 46 |       - name: Build and push hyper-mcp arch specific image
 47 |         run: |
 48 |           TAG=${GITHUB_REF#refs/tags/}
 49 |           hyper_mcp_image_arch="${{ env.REGISTRY }}/${{ github.repository_owner }}/hyper-mcp:$TAG-${{ matrix.arch }}"
 50 |           echo "Building and tagging arch specific image: $hyper_mcp_image_arch for ${{ matrix.arch }}"
 51 |           docker build -t $hyper_mcp_image_arch .
 52 |           docker push $hyper_mcp_image_arch
 53 |           cosign sign --yes $hyper_mcp_image_arch
 54 | 
 55 |       - name: Build and push plugin images (on amd64 only)
 56 |         if: matrix.arch == 'amd64'
 57 |         run: |
 58 |           TAG=${GITHUB_REF#refs/tags/}
 59 |           for plugin in examples/plugins/v{1,2}/*/; do
 60 |             plugin_name=$(basename $plugin)
 61 |             plugin_base_image="${{ env.REGISTRY }}/${{ github.repository_owner }}/${plugin_name}-plugin"
 62 | 
 63 |             echo "Building and tagging plugin: $plugin_name as $plugin_base_image:$TAG and $plugin_base_image:latest"
 64 |             docker build -t $plugin_base_image:$TAG -t $plugin_base_image:latest $plugin
 65 | 
 66 |             docker push $plugin_base_image:$TAG
 67 |             docker push $plugin_base_image:latest
 68 | 
 69 |             cosign sign --yes $plugin_base_image:$TAG
 70 |             cosign sign --yes $plugin_base_image:latest
 71 |           done
 72 | 
 73 |   create-multiarch-manifests:
 74 |     needs: build-oci-images
 75 |     runs-on: ubuntu-latest
 76 |     permissions:
 77 |       contents: read
 78 |       packages: write
 79 |       id-token: write # needed for keyless signing
 80 |     steps:
 81 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
 82 | 
 83 |       - name: Set up Docker Buildx
 84 |         uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
 85 | 
 86 |       - name: Install cosign
 87 |         uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
 88 | 
 89 |       - name: Log in to GitHub Container Registry
 90 |         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
 91 |         with:
 92 |           registry: ${{ env.REGISTRY }}
 93 |           username: ${{ github.actor }}
 94 |           password: ${{ secrets.GITHUB_TOKEN }}
 95 | 
 96 |       - name: Create and push multi-arch manifest for hyper-mcp
 97 |         run: |
 98 |           TAG=${GITHUB_REF#refs/tags/}
 99 |           hyper_mcp_base_image="${{ env.REGISTRY }}/${{ github.repository_owner }}/hyper-mcp"
100 | 
101 |           echo "Creating multi-arch manifest for $hyper_mcp_base_image:$TAG"
102 |           docker buildx imagetools create \
103 |             -t $hyper_mcp_base_image:$TAG \
104 |             $hyper_mcp_base_image:$TAG-amd64 \
105 |             $hyper_mcp_base_image:$TAG-arm64
106 |           cosign sign --yes $hyper_mcp_base_image:$TAG
107 | 
108 |           echo "Creating multi-arch manifest for $hyper_mcp_base_image:latest"
109 |           docker buildx imagetools create \
110 |             -t $hyper_mcp_base_image:latest \
111 |             $hyper_mcp_base_image:$TAG-amd64 \
112 |             $hyper_mcp_base_image:$TAG-arm64
113 |           cosign sign --yes $hyper_mcp_base_image:latest
114 | 
115 |   build-binaries:
116 |     strategy:
117 |       matrix:
118 |         include:
119 |           - os: ubuntu-latest
120 |             arch: x86_64
121 |             target: x86_64-unknown-linux-gnu
122 |           - os: ubuntu-24.04-arm
123 |             arch: aarch64
124 |             target: aarch64-unknown-linux-gnu
125 |           - os: macos-latest
126 |             arch: aarch64
127 |             target: aarch64-apple-darwin
128 | 
129 |     runs-on: ${{ matrix.os }}
130 |     permissions:
131 |       contents: write
132 |       packages: write
133 | 
134 |     steps:
135 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
136 |         with:
137 |           fetch-depth: 0
138 | 
139 |       - name: Install Rust toolchain
140 |         uses: dtolnay/rust-toolchain@stable
141 |         with:
142 |           components: rustfmt, clippy
143 | 
144 |       - run: cargo install cargo-auditable
145 | 
146 |       - name: Install target
147 |         run: rustup target add ${{ matrix.target }}
148 | 
149 |       - name: Build
150 |         run: cargo auditable build --target ${{ matrix.target }} --release
151 | 
152 |       - name: Create archives and checksums
153 |         run: |
154 |           mkdir -p dist/${{ matrix.target }}
155 |           cp target/${{ matrix.target }}/release/hyper-mcp dist/${{ matrix.target }}/
156 |           cd dist/${{ matrix.target }} && tar -czf ../hyper-mcp-${{ matrix.target }}.tar.gz hyper-mcp
157 |           cd ..
158 | 
159 |           {
160 |             echo "hyper-mcp-${{ matrix.target }}.tar.gz:"
161 |             if command -v sha256sum >/dev/null 2>&1; then
162 |               sha256sum hyper-mcp-${{ matrix.target }}.tar.gz
163 |             else
164 |               shasum -a 256 hyper-mcp-${{ matrix.target }}.tar.gz
165 |             fi
166 |           } > checksums.txt
167 | 
168 |       - name: Create GitHub Release
169 |         uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
170 |         with:
171 |           tag_name: ${{ github.ref_name }}
172 |           name: Release ${{ github.ref_name }}
173 |           draft: false
174 |           prerelease: false
175 |           files: |
176 |             dist/hyper-mcp-${{ matrix.target }}.tar.gz
177 |             dist/checksums.txt
178 |           body: |
179 |             Final release for `${{ github.ref_name }}`.
180 | 
181 |             Included:
182 |             - hyper-mcp binaries for Linux & macOS
183 |             - hyper-mcp container image: `ghcr.io/${{ github.repository_owner }}/hyper-mcp:${{ github.ref_name }}`
184 |             - Plugin images: `ghcr.io/${{ github.repository_owner }}/<plugin-name>-plugin:${{ github.ref_name }}`
185 | 
186 |             All container images are signed with Cosign. Verify with:
187 | 
188 |             ```bash
189 |             cosign verify \
190 |               --certificate-identity "https://github.com/tuananh/hyper-mcp/.github/workflows/release.yml@refs/tags/${{ github.ref_name }}" \
191 |               --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
192 |               ghcr.io/tuananh/hyper-mcp:${{ github.ref_name }}
193 |             ```
194 | 
```

--------------------------------------------------------------------------------
/examples/plugins/v1/tool-list-changed/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
  1 | // THIS FILE WAS GENERATED BY `xtp-rust-bindgen`. DO NOT EDIT.
  2 | 
  3 | #![allow(non_snake_case)]
  4 | #![allow(unused_macros)]
  5 | use extism_pdk::*;
  6 | 
  7 | #[allow(unused)]
  8 | fn panic_if_key_missing() -> ! {
  9 |     panic!("missing key");
 10 | }
 11 | 
 12 | pub(crate) mod internal {
 13 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 14 |         let err = format!("{:?}", e);
 15 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 16 |         unsafe {
 17 |             extism_pdk::extism::error_set(mem.offset());
 18 |         }
 19 |         -1
 20 |     }
 21 | }
 22 | 
 23 | #[allow(unused)]
 24 | macro_rules! try_input {
 25 |     () => {{
 26 |         let x = extism_pdk::input();
 27 |         match x {
 28 |             Ok(x) => x,
 29 |             Err(e) => return internal::return_error(e),
 30 |         }
 31 |     }};
 32 | }
 33 | 
 34 | #[allow(unused)]
 35 | macro_rules! try_input_json {
 36 |     () => {{
 37 |         let x = extism_pdk::input();
 38 |         match x {
 39 |             Ok(extism_pdk::Json(x)) => x,
 40 |             Err(e) => return internal::return_error(e),
 41 |         }
 42 |     }};
 43 | }
 44 | 
 45 | use base64_serde::base64_serde_type;
 46 | 
 47 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 48 | 
 49 | mod exports {
 50 |     use super::*;
 51 | 
 52 |     #[unsafe(no_mangle)]
 53 |     pub extern "C" fn call() -> i32 {
 54 |         let ret =
 55 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 56 | 
 57 |         match ret {
 58 |             Ok(()) => 0,
 59 |             Err(e) => internal::return_error(e),
 60 |         }
 61 |     }
 62 | 
 63 |     #[unsafe(no_mangle)]
 64 |     pub extern "C" fn describe() -> i32 {
 65 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 66 | 
 67 |         match ret {
 68 |             Ok(()) => 0,
 69 |             Err(e) => internal::return_error(e),
 70 |         }
 71 |     }
 72 | }
 73 | 
 74 | pub mod types {
 75 |     use super::*;
 76 | 
 77 |     #[derive(
 78 |         Default,
 79 |         Debug,
 80 |         Clone,
 81 |         serde::Serialize,
 82 |         serde::Deserialize,
 83 |         extism_pdk::FromBytes,
 84 |         extism_pdk::ToBytes,
 85 |     )]
 86 |     #[encoding(Json)]
 87 |     pub struct CallToolRequest {
 88 |         #[serde(rename = "method")]
 89 |         #[serde(skip_serializing_if = "Option::is_none")]
 90 |         #[serde(default)]
 91 |         pub method: Option<String>,
 92 | 
 93 |         #[serde(rename = "params")]
 94 |         pub params: types::Params,
 95 |     }
 96 | 
 97 |     #[derive(
 98 |         Default,
 99 |         Debug,
100 |         Clone,
101 |         serde::Serialize,
102 |         serde::Deserialize,
103 |         extism_pdk::FromBytes,
104 |         extism_pdk::ToBytes,
105 |     )]
106 |     #[encoding(Json)]
107 |     pub struct CallToolResult {
108 |         #[serde(rename = "content")]
109 |         pub content: Vec<types::Content>,
110 | 
111 |         /// Whether the tool call ended in an error.
112 |         ///
113 |         /// If not set, this is assumed to be false (the call was successful).
114 |         #[serde(rename = "isError")]
115 |         #[serde(skip_serializing_if = "Option::is_none")]
116 |         #[serde(default)]
117 |         pub is_error: Option<bool>,
118 |     }
119 | 
120 |     #[derive(
121 |         Default,
122 |         Debug,
123 |         Clone,
124 |         serde::Serialize,
125 |         serde::Deserialize,
126 |         extism_pdk::FromBytes,
127 |         extism_pdk::ToBytes,
128 |     )]
129 |     #[encoding(Json)]
130 |     pub struct Content {
131 |         #[serde(rename = "annotations")]
132 |         #[serde(skip_serializing_if = "Option::is_none")]
133 |         #[serde(default)]
134 |         pub annotations: Option<types::TextAnnotation>,
135 | 
136 |         /// The base64-encoded image data.
137 |         #[serde(rename = "data")]
138 |         #[serde(skip_serializing_if = "Option::is_none")]
139 |         #[serde(default)]
140 |         pub data: Option<String>,
141 | 
142 |         /// The MIME type of the image. Different providers may support different image types.
143 |         #[serde(rename = "mimeType")]
144 |         #[serde(skip_serializing_if = "Option::is_none")]
145 |         #[serde(default)]
146 |         pub mime_type: Option<String>,
147 | 
148 |         /// The text content of the message.
149 |         #[serde(rename = "text")]
150 |         #[serde(skip_serializing_if = "Option::is_none")]
151 |         #[serde(default)]
152 |         pub text: Option<String>,
153 | 
154 |         #[serde(rename = "type")]
155 |         pub r#type: types::ContentType,
156 |     }
157 | 
158 |     #[derive(
159 |         Default,
160 |         Debug,
161 |         Clone,
162 |         serde::Serialize,
163 |         serde::Deserialize,
164 |         extism_pdk::FromBytes,
165 |         extism_pdk::ToBytes,
166 |     )]
167 |     #[encoding(Json)]
168 |     pub enum ContentType {
169 |         #[default]
170 |         #[serde(rename = "text")]
171 |         Text,
172 |         #[serde(rename = "image")]
173 |         Image,
174 |         #[serde(rename = "resource")]
175 |         Resource,
176 |     }
177 | 
178 |     #[derive(
179 |         Default,
180 |         Debug,
181 |         Clone,
182 |         serde::Serialize,
183 |         serde::Deserialize,
184 |         extism_pdk::FromBytes,
185 |         extism_pdk::ToBytes,
186 |     )]
187 |     #[encoding(Json)]
188 |     pub struct ListToolsResult {
189 |         /// The list of ToolDescription objects provided by this servlet.
190 |         #[serde(rename = "tools")]
191 |         pub tools: Vec<types::ToolDescription>,
192 |     }
193 | 
194 |     #[derive(
195 |         Default,
196 |         Debug,
197 |         Clone,
198 |         serde::Serialize,
199 |         serde::Deserialize,
200 |         extism_pdk::FromBytes,
201 |         extism_pdk::ToBytes,
202 |     )]
203 |     #[encoding(Json)]
204 |     pub struct Params {
205 |         #[serde(rename = "arguments")]
206 |         #[serde(skip_serializing_if = "Option::is_none")]
207 |         #[serde(default)]
208 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
209 | 
210 |         #[serde(rename = "name")]
211 |         pub name: String,
212 |     }
213 | 
214 |     #[derive(
215 |         Default,
216 |         Debug,
217 |         Clone,
218 |         serde::Serialize,
219 |         serde::Deserialize,
220 |         extism_pdk::FromBytes,
221 |         extism_pdk::ToBytes,
222 |     )]
223 |     #[encoding(Json)]
224 |     pub enum Role {
225 |         #[default]
226 |         #[serde(rename = "assistant")]
227 |         Assistant,
228 |         #[serde(rename = "user")]
229 |         User,
230 |     }
231 | 
232 |     #[derive(
233 |         Default,
234 |         Debug,
235 |         Clone,
236 |         serde::Serialize,
237 |         serde::Deserialize,
238 |         extism_pdk::FromBytes,
239 |         extism_pdk::ToBytes,
240 |     )]
241 |     #[encoding(Json)]
242 |     pub struct TextAnnotation {
243 |         /// Describes who the intended customer of this object or data is.
244 |         ///
245 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
246 |         #[serde(rename = "audience")]
247 |         pub audience: Vec<types::Role>,
248 | 
249 |         /// Describes how important this data is for operating the server.
250 |         ///
251 |         /// A value of 1 means "most important," and indicates that the data is
252 |         /// effectively required, while 0 means "least important," and indicates that
253 |         /// the data is entirely optional.
254 |         #[serde(rename = "priority")]
255 |         pub priority: f32,
256 |     }
257 | 
258 |     #[derive(
259 |         Default,
260 |         Debug,
261 |         Clone,
262 |         serde::Serialize,
263 |         serde::Deserialize,
264 |         extism_pdk::FromBytes,
265 |         extism_pdk::ToBytes,
266 |     )]
267 |     #[encoding(Json)]
268 |     pub struct ToolDescription {
269 |         /// A description of the tool
270 |         #[serde(rename = "description")]
271 |         pub description: String,
272 | 
273 |         /// The JSON schema describing the argument input
274 |         #[serde(rename = "inputSchema")]
275 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
276 | 
277 |         /// The name of the tool. It should match the plugin / binding name.
278 |         #[serde(rename = "name")]
279 |         pub name: String,
280 |     }
281 | }
282 | 
```

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

```rust
  1 | mod pdk;
  2 | 
  3 | use chrono::{DateTime, Utc};
  4 | use extism_pdk::*;
  5 | use pdk::types::{
  6 |     CallToolRequest, CallToolResult, Content, ContentType, ListToolsResult, Role, TextAnnotation,
  7 |     ToolDescription,
  8 | };
  9 | use serde::{Deserialize, Serialize};
 10 | use serde_json::json;
 11 | #[derive(Debug, Serialize, Deserialize)]
 12 | struct Paper {
 13 |     paper_id: String,
 14 |     title: String,
 15 |     authors: Vec<String>,
 16 |     abstract_text: String,
 17 |     url: String,
 18 |     pdf_url: String,
 19 |     published_date: DateTime<Utc>,
 20 |     updated_date: DateTime<Utc>,
 21 |     source: String,
 22 |     categories: Vec<String>,
 23 |     keywords: Vec<String>,
 24 |     doi: String,
 25 | }
 26 | 
 27 | pub(crate) fn call(input: CallToolRequest) -> Result<CallToolResult, Error> {
 28 |     match input.params.name.as_str() {
 29 |         "arxiv_search" => search(input),
 30 |         "arxiv_download_pdf" => download_pdf(input),
 31 |         _ => Ok(CallToolResult {
 32 |             is_error: Some(true),
 33 |             content: vec![Content {
 34 |                 annotations: None,
 35 |                 text: Some(format!("Unknown tool: {}", input.params.name)),
 36 |                 mime_type: None,
 37 |                 r#type: ContentType::Text,
 38 |                 data: None,
 39 |             }],
 40 |         }),
 41 |     }
 42 | }
 43 | 
 44 | fn search(input: CallToolRequest) -> Result<CallToolResult, Error> {
 45 |     let args = input.params.arguments.unwrap_or_default();
 46 |     let query = match args.get("query") {
 47 |         Some(v) if v.is_string() => v.as_str().unwrap(),
 48 |         _ => return Err(Error::msg("query parameter is required")),
 49 |     };
 50 | 
 51 |     let max_results = args
 52 |         .get("max_results")
 53 |         .and_then(|v| v.as_u64())
 54 |         .unwrap_or(10);
 55 | 
 56 |     let req = HttpRequest {
 57 |         url: format!(
 58 |             "http://export.arxiv.org/api/query?search_query={}&max_results={}&sortBy=submittedDate&sortOrder=descending",
 59 |             urlencoding::encode(query),
 60 |             max_results
 61 |         ),
 62 |         headers: [(
 63 |             "User-Agent".to_string(),
 64 |             "hyper-mcp/1.0 (https://github.com/tuananh/hyper-mcp)".to_string(),
 65 |         )]
 66 |         .into_iter()
 67 |         .collect(),
 68 |         method: Some("GET".to_string()),
 69 |     };
 70 | 
 71 |     let res = http::request::<()>(&req, None)?;
 72 | 
 73 |     let body = res.body();
 74 |     let xml = String::from_utf8_lossy(body.as_slice());
 75 | 
 76 |     let feed = match feed_rs::parser::parse(xml.as_bytes()) {
 77 |         Ok(feed) => feed,
 78 |         Err(e) => return Err(Error::msg(format!("Failed to parse arXiv feed: {}", e))),
 79 |     };
 80 | 
 81 |     let mut papers = Vec::new();
 82 |     for entry in feed.entries {
 83 |         let paper_id = entry.id.split("/abs/").last().unwrap_or("").to_string();
 84 | 
 85 |         let authors = entry
 86 |             .authors
 87 |             .iter()
 88 |             .map(|author| author.name.clone())
 89 |             .collect();
 90 | 
 91 |         let categories = entry
 92 |             .categories
 93 |             .iter()
 94 |             .map(|cat| cat.term.clone())
 95 |             .collect();
 96 | 
 97 |         papers.push(Paper {
 98 |             paper_id,
 99 |             title: entry.title.map(|t| t.content).unwrap_or_default(),
100 |             authors,
101 |             abstract_text: entry.content.and_then(|c| c.body).unwrap_or_default(),
102 |             url: entry
103 |                 .links
104 |                 .iter()
105 |                 .find(|l| l.rel == Some("alternate".to_string()))
106 |                 .map(|l| l.href.clone())
107 |                 .unwrap_or_default(),
108 |             pdf_url: entry
109 |                 .links
110 |                 .iter()
111 |                 .find(|l| l.media_type.as_deref() == Some("application/pdf"))
112 |                 .map(|l| l.href.clone())
113 |                 .unwrap_or_default(),
114 |             published_date: entry.published.unwrap_or_default(),
115 |             updated_date: entry.updated.unwrap_or_default(),
116 |             source: "arxiv".to_string(),
117 |             categories,
118 |             keywords: Vec::new(),
119 |             doi: String::new(),
120 |         });
121 |     }
122 | 
123 |     Ok(CallToolResult {
124 |         is_error: None,
125 |         content: vec![Content {
126 |             annotations: None,
127 |             text: Some(serde_json::to_string(&papers)?),
128 |             mime_type: Some("application/json".to_string()),
129 |             r#type: ContentType::Text,
130 |             data: None,
131 |         }],
132 |     })
133 | }
134 | 
135 | fn download_pdf(input: CallToolRequest) -> Result<CallToolResult, Error> {
136 |     let args = input.params.arguments.unwrap_or_default();
137 |     let paper_id = match args.get("paper_id") {
138 |         Some(v) if v.is_string() => v.as_str().unwrap(),
139 |         _ => return Err(Error::msg("paper_id parameter is required")),
140 |     };
141 | 
142 |     // Get the path parameter with default to /tmp
143 |     let save_path = args
144 |         .get("save_path")
145 |         .and_then(|v| v.as_str())
146 |         .unwrap_or("/tmp");
147 | 
148 |     // Clean up the paper ID in case it contains the full URL
149 |     let clean_paper_id = if paper_id.contains("/") {
150 |         paper_id.split('/').next_back().unwrap_or(paper_id)
151 |     } else {
152 |         paper_id
153 |     };
154 | 
155 |     let url = format!("https://arxiv.org/pdf/{}", clean_paper_id);
156 | 
157 |     let req = HttpRequest {
158 |         url,
159 |         headers: [
160 |             (
161 |                 "User-Agent".to_string(),
162 |                 "Mozilla/5.0 (compatible; hyper-mcp/1.0)".to_string(),
163 |             ),
164 |             ("Accept".to_string(), "application/pdf".to_string()),
165 |         ]
166 |         .into_iter()
167 |         .collect(),
168 |         method: Some("GET".to_string()),
169 |     };
170 | 
171 |     let res = match http::request::<()>(&req, None) {
172 |         Ok(r) => r,
173 |         Err(e) => return Err(Error::msg(format!("HTTP request failed: {}", e))),
174 |     };
175 | 
176 |     let pdf_data = res.body();
177 |     if pdf_data.is_empty() {
178 |         return Err(Error::msg("Received empty PDF data from arXiv"));
179 |     }
180 | 
181 |     let file_path = format!("{}/{}.pdf", save_path.trim_end_matches('/'), clean_paper_id);
182 |     match std::fs::write(&file_path, &pdf_data) {
183 |         Ok(_) => (),
184 |         Err(e) => {
185 |             return Err(Error::msg(format!(
186 |                 "Failed to write PDF to {}: {}",
187 |                 file_path, e
188 |             )));
189 |         }
190 |     }
191 | 
192 |     // let pdf_base64 = base64::engine::general_purpose::STANDARD.encode(pdf_data);
193 | 
194 |     // TODO: actually return a resource
195 |     Ok(CallToolResult {
196 |         is_error: None,
197 |         content: vec![Content {
198 |             annotations: Some(TextAnnotation {
199 |                 audience: vec![Role::User, Role::Assistant],
200 |                 priority: 1.0,
201 |             }),
202 |             text: Some(format!("PDF saved to: {}", file_path)),
203 |             mime_type: None,
204 |             data: None,
205 |             r#type: ContentType::Text,
206 |         }],
207 |     })
208 | }
209 | 
210 | pub(crate) fn describe() -> Result<ListToolsResult, Error> {
211 |     Ok(ListToolsResult {
212 |         tools: vec![
213 |             ToolDescription {
214 |                 name: "arxiv_search".into(),
215 |                 description: "Search for papers on arXiv".into(),
216 |                 input_schema: json!({
217 |                     "type": "object",
218 |                     "properties": {
219 |                         "query": {
220 |                             "type": "string",
221 |                             "description": "The search query",
222 |                         },
223 |                         "max_results": {
224 |                             "type": "integer",
225 |                             "description": "Maximum number of results to return (default: 10)",
226 |                         }
227 |                     },
228 |                     "required": ["query"],
229 |                 })
230 |                 .as_object()
231 |                 .unwrap()
232 |                 .clone(),
233 |             },
234 |             ToolDescription {
235 |                 name: "arxiv_download_pdf".into(),
236 |                 description: "Download a paper's PDF from arXiv".into(),
237 |                 input_schema: json!({
238 |                     "type": "object",
239 |                     "properties": {
240 |                         "paper_id": {
241 |                             "type": "string",
242 |                             "description": "The arXiv paper ID",
243 |                         },
244 |                         "save_path": {
245 |                             "type": "string",
246 |                             "description": "Path to save the PDF file (default: /tmp)",
247 |                         }
248 |                     },
249 |                     "required": ["paper_id"],
250 |                 })
251 |                 .as_object()
252 |                 .unwrap()
253 |                 .clone(),
254 |             },
255 |         ],
256 |     })
257 | }
258 | 
```

--------------------------------------------------------------------------------
/.github/workflows/nightly.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: Nightly Release
  2 | 
  3 | on:
  4 |   schedule:
  5 |     - cron: "0 17 * * *" # midnight GMT+7
  6 |   workflow_dispatch:
  7 | 
  8 | env:
  9 |   CARGO_TERM_COLOR: always
 10 | 
 11 | jobs:
 12 |   build-oci-images:
 13 |     strategy:
 14 |       matrix:
 15 |         include:
 16 |           - os: ubuntu-24.04
 17 |             arch: amd64
 18 |           - os: ubuntu-24.04-arm
 19 |             arch: arm64
 20 |     runs-on: ${{ matrix.os }}
 21 |     permissions:
 22 |       contents: write
 23 |       packages: write
 24 |       id-token: write # needed for keyless signing
 25 | 
 26 |     steps:
 27 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
 28 |         with:
 29 |           fetch-depth: 0
 30 |           submodules: true
 31 | 
 32 |       - name: Set up Docker Buildx
 33 |         uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
 34 | 
 35 |       - name: Install cosign
 36 |         uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
 37 | 
 38 |       - name: Log in to GitHub Container Registry
 39 |         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
 40 |         with:
 41 |           registry: ghcr.io
 42 |           username: ${{ github.actor }}
 43 |           password: ${{ secrets.GITHUB_TOKEN }}
 44 | 
 45 |       - name: Log in to DockerHub Registry
 46 |         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
 47 |         with:
 48 |           registry: docker.io
 49 |           username: ${{ secrets.DOCKER_HUB_USERNAME }}
 50 |           password: ${{ secrets.DOCKER_HUB_TOKEN }}
 51 | 
 52 |       - name: Build and push hyper-mcp
 53 |         run: |
 54 |           echo "Building hyper-mcp image"
 55 |           ghcr_image="ghcr.io/${{ github.repository_owner }}/hyper-mcp:nightly-${{ matrix.arch }}"
 56 |           dockerhub_image="docker.io/tuananh/hyper-mcp:nightly-${{ matrix.arch }}"
 57 |           docker build -t $ghcr_image -t $dockerhub_image .
 58 | 
 59 |           docker push $ghcr_image
 60 |           docker push $dockerhub_image
 61 | 
 62 |           cosign sign --yes $ghcr_image
 63 |           cosign sign --yes $dockerhub_image
 64 | 
 65 |       # we dont need to build multi-arch plugin images as they are wasm32-wasip1
 66 |       # so we can just build amd64 and push it to the registry
 67 |       - name: Build and push plugin images
 68 |         if: matrix.arch == 'amd64'
 69 |         run: |
 70 |           for plugin in examples/plugins/v{1,2}/*/; do
 71 |             plugin_name=$(basename $plugin)
 72 |             echo "Building plugin: $plugin_name"
 73 | 
 74 |             image_name="ghcr.io/${{ github.repository_owner }}/${plugin_name}-plugin:nightly"
 75 |             docker build -t $image_name $plugin
 76 |             docker push $image_name
 77 | 
 78 |             cosign sign --yes $image_name
 79 |           done
 80 | 
 81 |   # do this before we build nightly binaries
 82 |   prepare-nightly-release:
 83 |     runs-on: ubuntu-latest
 84 |     permissions:
 85 |       contents: write
 86 |     steps:
 87 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
 88 |         with:
 89 |           fetch-depth: 0
 90 | 
 91 |       - name: Set nightly tag to latest main
 92 |         run: |
 93 |           git fetch origin main
 94 |           git tag -f nightly origin/main
 95 |           git push -f origin nightly
 96 | 
 97 |       - name: Delete existing nightly release
 98 |         run: gh release delete nightly --yes || true
 99 | 
100 |   build-nightly-binaries:
101 |     needs: prepare-nightly-release
102 |     strategy:
103 |       matrix:
104 |         include:
105 |           - os: ubuntu-24.04
106 |             arch: x86_64
107 |             target: x86_64-unknown-linux-gnu
108 |             ext: ""
109 |           - os: ubuntu-24.04-arm
110 |             arch: aarch64
111 |             target: aarch64-unknown-linux-gnu
112 |             ext: ""
113 |           - os: macos-latest
114 |             arch: aarch64
115 |             target: aarch64-apple-darwin
116 |             ext: ""
117 |           - os: windows-latest
118 |             arch: x86_64
119 |             target: x86_64-pc-windows-msvc
120 |             ext: ".exe"
121 | 
122 |     runs-on: ${{ matrix.os }}
123 |     permissions:
124 |       contents: write
125 |       packages: write
126 | 
127 |     steps:
128 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
129 |         with:
130 |           fetch-depth: 0
131 | 
132 |       - name: Install Rust toolchain
133 |         uses: dtolnay/rust-toolchain@stable
134 |         with:
135 |           components: rustfmt, clippy
136 | 
137 |       - run: cargo install cargo-auditable
138 | 
139 |       - name: Install compilation targets
140 |         run: rustup target add ${{ matrix.target }}
141 | 
142 |       - name: Build
143 |         run: cargo auditable build --target ${{ matrix.target }} --release
144 | 
145 |       - name: Install zip (Windows only)
146 |         if: runner.os == 'Windows'
147 |         run: choco install zip -y
148 | 
149 |       - name: Create archives and checksums
150 |         shell: bash
151 |         run: |
152 |           mkdir -p dist/${{ matrix.target }}
153 | 
154 |           bin_name="hyper-mcp${{ matrix.ext }}"
155 |           cp target/${{ matrix.target }}/release/$bin_name dist/${{ matrix.target }}/
156 | 
157 |           cd dist/${{ matrix.target }}
158 |           if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
159 |             zip ../hyper-mcp-${{ matrix.target }}.zip $bin_name
160 |           else
161 |             tar -czf ../hyper-mcp-${{ matrix.target }}.tar.gz $bin_name
162 |           fi
163 |           cd ..
164 | 
165 |           {
166 |             if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
167 |               file="hyper-mcp-${{ matrix.target }}.zip"
168 |             else
169 |               file="hyper-mcp-${{ matrix.target }}.tar.gz"
170 |             fi
171 |             echo "$file:"
172 |             if command -v sha256sum >/dev/null 2>&1; then
173 |               sha256sum $file
174 |             else
175 |               shasum -a 256 $file
176 |             fi
177 |           } > checksums-${{ matrix.target }}.txt
178 | 
179 |       - name: Create new nightly release
180 |         id: create_release
181 |         uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
182 |         with:
183 |           tag_name: nightly
184 |           name: Nightly build
185 |           draft: false
186 |           prerelease: true
187 |           files: |
188 |             dist/hyper-mcp-${{ matrix.target }}.tar.gz
189 |             dist/hyper-mcp-${{ matrix.target }}.zip
190 |             dist/checksums-${{ matrix.target }}.txt
191 |           body: |
192 |             Nightly build from `main` branch.
193 | 
194 |             This release includes:
195 |             - hyper-mcp binaries for Linux & macOS
196 |             - hyper-mcp container image: `ghcr.io/${{ github.repository_owner }}/hyper-mcp:nightly`
197 |             - Plugin images: `ghcr.io/${{ github.repository_owner }}/<plugin-name>-plugin:nightly`
198 | 
199 |             All container images are signed with Cosign. Verify the image like this:
200 |             ```bash
201 |             cosign verify \
202 |               --certificate-identity "https://github.com/tuananh/hyper-mcp/.github/workflows/nightly.yml@refs/heads/main" \
203 |               --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
204 |               ghcr.io/tuananh/hyper-mcp:nightly
205 |             ```
206 | 
207 |   create-multiarch-manifests:
208 |     needs: build-oci-images
209 |     runs-on: ubuntu-latest
210 |     permissions:
211 |       contents: write
212 |       packages: write
213 |       id-token: write # needed for keyless signing
214 |     steps:
215 |       - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
216 | 
217 |       - name: Set up Docker Buildx
218 |         uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
219 | 
220 |       - name: Install cosign
221 |         uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
222 | 
223 |       - name: Log in to GitHub Container Registry
224 |         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
225 |         with:
226 |           registry: ghcr.io
227 |           username: ${{ github.actor }}
228 |           password: ${{ secrets.GITHUB_TOKEN }}
229 | 
230 |       - name: Log in to DockerHub Registry
231 |         uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
232 |         with:
233 |           registry: docker.io
234 |           username: ${{ secrets.DOCKER_HUB_USERNAME }}
235 |           password: ${{ secrets.DOCKER_HUB_TOKEN }}
236 | 
237 |       - name: Create and push multi-arch nightly tags
238 |         run: |
239 |           # Main image
240 |           docker buildx imagetools create \
241 |             -t ghcr.io/${{ github.repository_owner }}/hyper-mcp:nightly \
242 |             ghcr.io/${{ github.repository_owner }}/hyper-mcp:nightly-amd64 \
243 |             ghcr.io/${{ github.repository_owner }}/hyper-mcp:nightly-arm64
244 | 
245 |           cosign sign --yes ghcr.io/${{ github.repository_owner }}/hyper-mcp:nightly
246 | 
247 |           # DockerHub
248 |           docker buildx imagetools create \
249 |             -t tuananh/hyper-mcp:nightly \
250 |             tuananh/hyper-mcp:nightly-amd64 \
251 |             tuananh/hyper-mcp:nightly-arm64
252 | 
253 |           cosign sign --yes tuananh/hyper-mcp:nightly
254 | 
```

--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/src/lib.rs:
--------------------------------------------------------------------------------

```rust
  1 | mod pdk;
  2 | 
  3 | use std::collections::BTreeMap;
  4 | 
  5 | use extism_pdk::*;
  6 | use json::Value;
  7 | use pdk::types::{
  8 |     CallToolRequest, CallToolResult, Content, ContentType, ListToolsResult, ToolDescription,
  9 | };
 10 | use serde_json::json;
 11 | 
 12 | pub(crate) fn call(input: CallToolRequest) -> Result<CallToolResult, Error> {
 13 |     match input.params.name.as_str() {
 14 |         "crates_io_latest_version" => latest_version(input),
 15 |         "crates_io_crate_info" => crate_info(input),
 16 |         _ => Ok(CallToolResult {
 17 |             is_error: Some(true),
 18 |             content: vec![Content {
 19 |                 annotations: None,
 20 |                 text: Some(format!("Unknown tool: {}", input.params.name)),
 21 |                 mime_type: None,
 22 |                 r#type: ContentType::Text,
 23 |                 data: None,
 24 |             }],
 25 |         }),
 26 |     }
 27 | }
 28 | 
 29 | fn crate_info(input: CallToolRequest) -> Result<CallToolResult, Error> {
 30 |     let args = input.params.arguments.unwrap_or_default();
 31 |     if let Some(Value::String(crate_names)) = args.get("crate_names") {
 32 |         let crate_names: Vec<&str> = crate_names.split(',').map(|s| s.trim()).collect();
 33 |         let mut results = Vec::new();
 34 | 
 35 |         for crate_name in crate_names {
 36 |             // Create HTTP request to crates.io API
 37 |             let mut req = HttpRequest {
 38 |                 url: format!("https://crates.io/api/v1/crates/{}", crate_name),
 39 |                 headers: BTreeMap::new(),
 40 |                 method: Some("GET".to_string()),
 41 |             };
 42 | 
 43 |             // Add a user agent header to be polite
 44 |             req.headers
 45 |                 .insert("User-Agent".to_string(), "crates-io-tool/1.0".to_string());
 46 | 
 47 |             // Perform the request
 48 |             let res = http::request::<()>(&req, None)?;
 49 | 
 50 |             // Convert response body to string
 51 |             let body = res.body();
 52 |             let json_str = String::from_utf8_lossy(body.as_slice());
 53 | 
 54 |             // Parse the JSON response
 55 |             let json: serde_json::Value = serde_json::from_str(&json_str)?;
 56 | 
 57 |             if let Some(crate_info) = json["crate"].as_object() {
 58 |                 // Extract relevant information with null checks
 59 |                 let info = json!({
 60 |                     "name": crate_info.get("name").and_then(|v| v.as_str()),
 61 |                     "description": crate_info.get("description").and_then(|v| v.as_str()),
 62 |                     "latest_version": crate_info.get("max_version").and_then(|v| v.as_str()),
 63 |                     "downloads": crate_info.get("downloads").and_then(|v| v.as_i64()),
 64 |                     "repository": crate_info.get("repository").and_then(|v| v.as_str()),
 65 |                     "documentation": crate_info.get("documentation").and_then(|v| v.as_str()),
 66 |                     "homepage": crate_info.get("homepage").and_then(|v| v.as_str()),
 67 |                     "keywords": crate_info.get("keywords").and_then(|v| v.as_array()),
 68 |                     "categories": crate_info.get("categories").and_then(|v| v.as_array()),
 69 |                     "license": json["versions"].as_array().and_then(|v| v.first()).and_then(|v| v["license"].as_str()),
 70 |                     "created_at": crate_info.get("created_at").and_then(|v| v.as_str()),
 71 |                     "updated_at": crate_info.get("updated_at").and_then(|v| v.as_str()),
 72 |                 });
 73 | 
 74 |                 results.push(info);
 75 |             }
 76 |         }
 77 | 
 78 |         if !results.is_empty() {
 79 |             Ok(CallToolResult {
 80 |                 is_error: None,
 81 |                 content: vec![Content {
 82 |                     annotations: None,
 83 |                     text: Some(serde_json::to_string(&results)?),
 84 |                     mime_type: Some("text/plain".to_string()),
 85 |                     r#type: ContentType::Text,
 86 |                     data: None,
 87 |                 }],
 88 |             })
 89 |         } else {
 90 |             Ok(CallToolResult {
 91 |                 is_error: Some(true),
 92 |                 content: vec![Content {
 93 |                     annotations: None,
 94 |                     text: Some("Failed to get crate information".into()),
 95 |                     mime_type: None,
 96 |                     r#type: ContentType::Text,
 97 |                     data: None,
 98 |                 }],
 99 |             })
100 |         }
101 |     } else {
102 |         Ok(CallToolResult {
103 |             is_error: Some(true),
104 |             content: vec![Content {
105 |                 annotations: None,
106 |                 text: Some("Please provide crate names".into()),
107 |                 mime_type: None,
108 |                 r#type: ContentType::Text,
109 |                 data: None,
110 |             }],
111 |         })
112 |     }
113 | }
114 | 
115 | fn latest_version(input: CallToolRequest) -> Result<CallToolResult, Error> {
116 |     let args = input.params.arguments.unwrap_or_default();
117 |     if let Some(Value::String(crate_names)) = args.get("crate_names") {
118 |         let crate_names: Vec<&str> = crate_names.split(',').map(|s| s.trim()).collect();
119 |         let mut results = BTreeMap::new();
120 | 
121 |         for crate_name in crate_names {
122 |             // Create HTTP request to crates.io API
123 |             let mut req = HttpRequest {
124 |                 url: format!("https://crates.io/api/v1/crates/{}", crate_name),
125 |                 headers: BTreeMap::new(),
126 |                 method: Some("GET".to_string()),
127 |             };
128 | 
129 |             // Add a user agent header to be polite
130 |             req.headers
131 |                 .insert("User-Agent".to_string(), "crates-io-tool/1.0".to_string());
132 | 
133 |             // Perform the request
134 |             let res = http::request::<()>(&req, None)?;
135 | 
136 |             // Convert response body to string
137 |             let body = res.body();
138 |             let json_str = String::from_utf8_lossy(body.as_slice());
139 | 
140 |             // Parse the JSON response
141 |             let json: serde_json::Value = serde_json::from_str(&json_str)?;
142 | 
143 |             if let Some(version) = json["crate"]["max_version"].as_str() {
144 |                 results.insert(crate_name.to_string(), version.to_string());
145 |             }
146 |         }
147 | 
148 |         if !results.is_empty() {
149 |             Ok(CallToolResult {
150 |                 is_error: None,
151 |                 content: vec![Content {
152 |                     annotations: None,
153 |                     text: Some(serde_json::to_string(&results)?),
154 |                     mime_type: Some("text/plain".to_string()),
155 |                     r#type: ContentType::Text,
156 |                     data: None,
157 |                 }],
158 |             })
159 |         } else {
160 |             Ok(CallToolResult {
161 |                 is_error: Some(true),
162 |                 content: vec![Content {
163 |                     annotations: None,
164 |                     text: Some("Failed to get latest versions".into()),
165 |                     mime_type: None,
166 |                     r#type: ContentType::Text,
167 |                     data: None,
168 |                 }],
169 |             })
170 |         }
171 |     } else {
172 |         Ok(CallToolResult {
173 |             is_error: Some(true),
174 |             content: vec![Content {
175 |                 annotations: None,
176 |                 text: Some("Please provide crate names".into()),
177 |                 mime_type: None,
178 |                 r#type: ContentType::Text,
179 |                 data: None,
180 |             }],
181 |         })
182 |     }
183 | }
184 | 
185 | pub(crate) fn describe() -> Result<ListToolsResult, Error> {
186 |     Ok(ListToolsResult {
187 |         tools: vec![
188 |             ToolDescription {
189 |                 name: "crates_io_latest_version".into(),
190 |                 description: "Fetches the latest version of multiple crates from crates.io".into(),
191 |                 input_schema: json!({
192 |                     "type": "object",
193 |                     "properties": {
194 |                         "crate_names": {
195 |                             "type": "string",
196 |                             "description": "Comma-separated list of crate names to get the latest versions for",
197 |                         },
198 |                     },
199 |                     "required": ["crate_names"],
200 |                 })
201 |                 .as_object()
202 |                 .unwrap()
203 |                 .clone(),
204 |             },
205 |             ToolDescription {
206 |                 name: "crates_io_crate_info".into(),
207 |                 description: "Fetches detailed information about multiple crates from crates.io".into(),
208 |                 input_schema: json!({
209 |                     "type": "object",
210 |                     "properties": {
211 |                         "crate_names": {
212 |                             "type": "string",
213 |                             "description": "Comma-separated list of crate names to get information for",
214 |                         },
215 |                     },
216 |                     "required": ["crate_names"],
217 |                 })
218 |                 .as_object()
219 |                 .unwrap()
220 |                 .clone(),
221 |             },
222 |         ],
223 |     })
224 | }
225 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

--------------------------------------------------------------------------------
/examples/plugins/v1/eval-py/src/pdk.rs:
--------------------------------------------------------------------------------

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```

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

```rust
  1 | #![allow(non_snake_case)]
  2 | #![allow(unused_macros)]
  3 | use extism_pdk::*;
  4 | 
  5 | #[allow(unused)]
  6 | fn panic_if_key_missing() -> ! {
  7 |     panic!("missing key");
  8 | }
  9 | 
 10 | pub(crate) mod internal {
 11 |     pub(crate) fn return_error(e: extism_pdk::Error) -> i32 {
 12 |         let err = format!("{:?}", e);
 13 |         let mem = extism_pdk::Memory::from_bytes(&err).unwrap();
 14 |         unsafe {
 15 |             extism_pdk::extism::error_set(mem.offset());
 16 |         }
 17 |         -1
 18 |     }
 19 | }
 20 | 
 21 | #[allow(unused)]
 22 | macro_rules! try_input {
 23 |     () => {{
 24 |         let x = extism_pdk::input();
 25 |         match x {
 26 |             Ok(x) => x,
 27 |             Err(e) => return internal::return_error(e),
 28 |         }
 29 |     }};
 30 | }
 31 | 
 32 | #[allow(unused)]
 33 | macro_rules! try_input_json {
 34 |     () => {{
 35 |         let x = extism_pdk::input();
 36 |         match x {
 37 |             Ok(extism_pdk::Json(x)) => x,
 38 |             Err(e) => return internal::return_error(e),
 39 |         }
 40 |     }};
 41 | }
 42 | 
 43 | use base64_serde::base64_serde_type;
 44 | 
 45 | base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
 46 | 
 47 | mod exports {
 48 |     use super::*;
 49 | 
 50 |     #[unsafe(no_mangle)]
 51 |     pub extern "C" fn call() -> i32 {
 52 |         let ret =
 53 |             crate::call(try_input_json!()).and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 54 | 
 55 |         match ret {
 56 |             Ok(()) => 0,
 57 |             Err(e) => internal::return_error(e),
 58 |         }
 59 |     }
 60 | 
 61 |     #[unsafe(no_mangle)]
 62 |     pub extern "C" fn describe() -> i32 {
 63 |         let ret = crate::describe().and_then(|x| extism_pdk::output(extism_pdk::Json(x)));
 64 | 
 65 |         match ret {
 66 |             Ok(()) => 0,
 67 |             Err(e) => internal::return_error(e),
 68 |         }
 69 |     }
 70 | }
 71 | 
 72 | pub mod types {
 73 |     use super::*;
 74 | 
 75 |     #[derive(
 76 |         Default,
 77 |         Debug,
 78 |         Clone,
 79 |         serde::Serialize,
 80 |         serde::Deserialize,
 81 |         extism_pdk::FromBytes,
 82 |         extism_pdk::ToBytes,
 83 |     )]
 84 |     #[encoding(Json)]
 85 |     pub struct BlobResourceContents {
 86 |         /// A base64-encoded string representing the binary data of the item.
 87 |         #[serde(rename = "blob")]
 88 |         pub blob: String,
 89 | 
 90 |         /// The MIME type of this resource, if known.
 91 |         #[serde(rename = "mimeType")]
 92 |         #[serde(skip_serializing_if = "Option::is_none")]
 93 |         #[serde(default)]
 94 |         pub mime_type: Option<String>,
 95 | 
 96 |         /// The URI of this resource.
 97 |         #[serde(rename = "uri")]
 98 |         pub uri: String,
 99 |     }
100 | 
101 |     #[derive(
102 |         Default,
103 |         Debug,
104 |         Clone,
105 |         serde::Serialize,
106 |         serde::Deserialize,
107 |         extism_pdk::FromBytes,
108 |         extism_pdk::ToBytes,
109 |     )]
110 |     #[encoding(Json)]
111 |     pub struct CallToolRequest {
112 |         #[serde(rename = "method")]
113 |         #[serde(skip_serializing_if = "Option::is_none")]
114 |         #[serde(default)]
115 |         pub method: Option<String>,
116 | 
117 |         #[serde(rename = "params")]
118 |         pub params: types::Params,
119 |     }
120 | 
121 |     #[derive(
122 |         Default,
123 |         Debug,
124 |         Clone,
125 |         serde::Serialize,
126 |         serde::Deserialize,
127 |         extism_pdk::FromBytes,
128 |         extism_pdk::ToBytes,
129 |     )]
130 |     #[encoding(Json)]
131 |     pub struct CallToolResult {
132 |         #[serde(rename = "content")]
133 |         pub content: Vec<types::Content>,
134 | 
135 |         /// Whether the tool call ended in an error.
136 |         ///
137 |         /// If not set, this is assumed to be false (the call was successful).
138 |         #[serde(rename = "isError")]
139 |         #[serde(skip_serializing_if = "Option::is_none")]
140 |         #[serde(default)]
141 |         pub is_error: Option<bool>,
142 |     }
143 | 
144 |     #[derive(
145 |         Default,
146 |         Debug,
147 |         Clone,
148 |         serde::Serialize,
149 |         serde::Deserialize,
150 |         extism_pdk::FromBytes,
151 |         extism_pdk::ToBytes,
152 |     )]
153 |     #[encoding(Json)]
154 |     pub struct Content {
155 |         #[serde(rename = "annotations")]
156 |         #[serde(skip_serializing_if = "Option::is_none")]
157 |         #[serde(default)]
158 |         pub annotations: Option<types::TextAnnotation>,
159 | 
160 |         /// The base64-encoded image data.
161 |         #[serde(rename = "data")]
162 |         #[serde(skip_serializing_if = "Option::is_none")]
163 |         #[serde(default)]
164 |         pub data: Option<String>,
165 | 
166 |         /// The MIME type of the image. Different providers may support different image types.
167 |         #[serde(rename = "mimeType")]
168 |         #[serde(skip_serializing_if = "Option::is_none")]
169 |         #[serde(default)]
170 |         pub mime_type: Option<String>,
171 | 
172 |         /// The text content of the message.
173 |         #[serde(rename = "text")]
174 |         #[serde(skip_serializing_if = "Option::is_none")]
175 |         #[serde(default)]
176 |         pub text: Option<String>,
177 | 
178 |         #[serde(rename = "type")]
179 |         pub r#type: types::ContentType,
180 |     }
181 | 
182 |     #[derive(
183 |         Default,
184 |         Debug,
185 |         Clone,
186 |         serde::Serialize,
187 |         serde::Deserialize,
188 |         extism_pdk::FromBytes,
189 |         extism_pdk::ToBytes,
190 |     )]
191 |     #[encoding(Json)]
192 |     pub enum ContentType {
193 |         #[default]
194 |         #[serde(rename = "text")]
195 |         Text,
196 |         #[serde(rename = "image")]
197 |         Image,
198 |         #[serde(rename = "resource")]
199 |         Resource,
200 |     }
201 | 
202 |     #[derive(
203 |         Default,
204 |         Debug,
205 |         Clone,
206 |         serde::Serialize,
207 |         serde::Deserialize,
208 |         extism_pdk::FromBytes,
209 |         extism_pdk::ToBytes,
210 |     )]
211 |     #[encoding(Json)]
212 |     pub struct ListToolsResult {
213 |         /// The list of ToolDescription objects provided by this servlet.
214 |         #[serde(rename = "tools")]
215 |         pub tools: Vec<types::ToolDescription>,
216 |     }
217 | 
218 |     #[derive(
219 |         Default,
220 |         Debug,
221 |         Clone,
222 |         serde::Serialize,
223 |         serde::Deserialize,
224 |         extism_pdk::FromBytes,
225 |         extism_pdk::ToBytes,
226 |     )]
227 |     #[encoding(Json)]
228 |     pub struct Params {
229 |         #[serde(rename = "arguments")]
230 |         #[serde(skip_serializing_if = "Option::is_none")]
231 |         #[serde(default)]
232 |         pub arguments: Option<serde_json::Map<String, serde_json::Value>>,
233 | 
234 |         #[serde(rename = "name")]
235 |         pub name: String,
236 |     }
237 | 
238 |     #[derive(
239 |         Default,
240 |         Debug,
241 |         Clone,
242 |         serde::Serialize,
243 |         serde::Deserialize,
244 |         extism_pdk::FromBytes,
245 |         extism_pdk::ToBytes,
246 |     )]
247 |     #[encoding(Json)]
248 |     pub enum Role {
249 |         #[default]
250 |         #[serde(rename = "assistant")]
251 |         Assistant,
252 |         #[serde(rename = "user")]
253 |         User,
254 |     }
255 | 
256 |     #[derive(
257 |         Default,
258 |         Debug,
259 |         Clone,
260 |         serde::Serialize,
261 |         serde::Deserialize,
262 |         extism_pdk::FromBytes,
263 |         extism_pdk::ToBytes,
264 |     )]
265 |     #[encoding(Json)]
266 |     pub struct TextAnnotation {
267 |         /// Describes who the intended customer of this object or data is.
268 |         ///
269 |         /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
270 |         #[serde(rename = "audience")]
271 |         pub audience: Vec<types::Role>,
272 | 
273 |         /// Describes how important this data is for operating the server.
274 |         ///
275 |         /// A value of 1 means "most important," and indicates that the data is
276 |         /// effectively required, while 0 means "least important," and indicates that
277 |         /// the data is entirely optional.
278 |         #[serde(rename = "priority")]
279 |         pub priority: f32,
280 |     }
281 | 
282 |     #[derive(
283 |         Default,
284 |         Debug,
285 |         Clone,
286 |         serde::Serialize,
287 |         serde::Deserialize,
288 |         extism_pdk::FromBytes,
289 |         extism_pdk::ToBytes,
290 |     )]
291 |     #[encoding(Json)]
292 |     pub struct TextResourceContents {
293 |         /// The MIME type of this resource, if known.
294 |         #[serde(rename = "mimeType")]
295 |         #[serde(skip_serializing_if = "Option::is_none")]
296 |         #[serde(default)]
297 |         pub mime_type: Option<String>,
298 | 
299 |         /// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
300 |         #[serde(rename = "text")]
301 |         pub text: String,
302 | 
303 |         /// The URI of this resource.
304 |         #[serde(rename = "uri")]
305 |         pub uri: String,
306 |     }
307 | 
308 |     #[derive(
309 |         Default,
310 |         Debug,
311 |         Clone,
312 |         serde::Serialize,
313 |         serde::Deserialize,
314 |         extism_pdk::FromBytes,
315 |         extism_pdk::ToBytes,
316 |     )]
317 |     #[encoding(Json)]
318 |     pub struct ToolDescription {
319 |         /// A description of the tool
320 |         #[serde(rename = "description")]
321 |         pub description: String,
322 | 
323 |         /// The JSON schema describing the argument input
324 |         #[serde(rename = "inputSchema")]
325 |         pub input_schema: serde_json::Map<String, serde_json::Value>,
326 | 
327 |         /// The name of the tool. It should match the plugin / binding name.
328 |         #[serde(rename = "name")]
329 |         pub name: String,
330 |     }
331 | }
332 | 
333 | mod raw_imports {
334 |     use super::*;
335 |     #[host_fn]
336 |     extern "ExtismHost" {}
337 | }
338 | 
```
Page 3/11FirstPrevNextLast