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 |
```