This is page 1 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
--------------------------------------------------------------------------------
/examples/plugins/v1/github/.gitignore:
--------------------------------------------------------------------------------
```
1 | dist/
2 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/hash/.gitignore:
--------------------------------------------------------------------------------
```
1 | /target
2 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/myip/.gitignore:
--------------------------------------------------------------------------------
```
1 | /target
2 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qr-code/.gitignore:
--------------------------------------------------------------------------------
```
1 | /target
2 |
```
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
```
1 | [submodule "examples/plugins/v1/meme-generator/assets"]
2 | path = examples/plugins/v1/meme-generator/assets
3 | url = https://github.com/tuananh/meme-generator-assets.git
4 |
```
--------------------------------------------------------------------------------
/templates/plugins/go/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.so.*
7 | *.dylib
8 |
9 | # Test binary, built with `go test -c`
10 | *.test
11 |
12 | # Output of the go coverage tool
13 | *.out
14 |
15 | # Dependency directories
16 | vendor/
17 |
18 | # Go workspace file
19 | go.work
20 |
21 | # IDE
22 | .idea/
23 | .vscode/
24 | *.swp
25 | *.swo
26 | *~
27 | .DS_Store
28 |
29 | # Build output
30 | /bin/
31 | /dist/
32 | plugin.wasm
33 |
```
--------------------------------------------------------------------------------
/.hadolint.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # Output & failure behavior
2 | format: tty # default output format
3 | failure-threshold: warning # default: fail on >= info
4 | no-color: false
5 | no-fail: false # exit non-zero when threshold is met
6 |
7 | # Rule configuration
8 | ignored: [] # no rules ignored by default
9 | override: # no severity overrides by default
10 | error: []
11 | warning: []
12 | info: []
13 | style: []
14 |
15 | # Labels
16 | label-schema: {} # no required labels by default
17 | strict-labels: false # only check labels in schema when true
18 |
19 | # Misc
20 | disable-ignore-pragma: false # allow inline `# hadolint ignore=...`
21 | trustedRegistries: [] # none by default
22 |
```
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
```yaml
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v6.0.0
4 | hooks:
5 | - id: check-added-large-files
6 | args: ["--maxkb=2000"]
7 | - id: check-json
8 | - id: check-toml
9 | - id: check-yaml
10 | - id: end-of-file-fixer
11 | - id: trailing-whitespace
12 | - repo: https://github.com/backplane/pre-commit-rust-hooks
13 | rev: v1.2.1
14 | hooks:
15 | - id: fmt
16 | args: ["--"]
17 | - id: check
18 | - id: clippy
19 | args: ["--", "-D warnings"]
20 | - id: test
21 | stages: [manual]
22 | args: ["--workspace", "--all-features"]
23 | - repo: https://github.com/hadolint/hadolint
24 | rev: v2.14.0
25 | hooks:
26 | - id: hadolint
27 | stages: [manual]
28 | args: ["--no-color"]
29 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/arxiv/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/context7/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/eval-py/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fetch/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fs/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gitlab/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gomodule/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/maven/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/memory/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/serper/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/sqlite/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/think/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/time/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/tool-list-changed/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v2/rstime/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/templates/plugins/rust/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
```
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
```
1 | # Created by https://www.toptal.com/developers/gitignore/api/linux,rust,terraform
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=linux,rust,terraform
3 |
4 | ### Linux ###
5 | *~
6 |
7 | # temporary files which can be created if a process still has a handle open of a deleted file
8 | .fuse_hidden*
9 |
10 | # KDE directory preferences
11 | .directory
12 |
13 | # Linux trash folder which might appear on any partition or disk
14 | .Trash-*
15 |
16 | # .nfs files are created when an open file is removed but is still being accessed
17 | .nfs*
18 |
19 | ### Rust ###
20 | # Generated by Cargo
21 | # will have compiled files and executables
22 | debug/
23 | target/
24 |
25 | # These are backup files generated by rustfmt
26 | **/*.rs.bk
27 |
28 | # MSVC Windows builds of rustc generate these, which store debugging information
29 | *.pdb
30 |
31 | ### Terraform ###
32 | # Local .terraform directories
33 | **/.terraform/*
34 |
35 | # .tfstate files
36 | *.tfstate
37 | *.tfstate.*
38 |
39 | # Crash log files
40 | crash.log
41 | crash.*.log
42 |
43 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as
44 | # password, private keys, and other secrets. These should not be part of version
45 | # control as they are data points which are potentially sensitive and subject
46 | # to change depending on the environment.
47 | *.tfvars
48 | *.tfvars.json
49 |
50 | # Ignore override files as they are usually used to override resources locally and so
51 | # are not checked in
52 | override.tf
53 | override.tf.json
54 | *_override.tf
55 | *_override.tf.json
56 |
57 | # Include override files you do wish to add to version control using negated pattern
58 | # !example_override.tf
59 |
60 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
61 | # example: *tfplan*
62 |
63 | # Ignore CLI configuration files
64 | .terraformrc
65 | terraform.rc
66 |
67 | # End of https://www.toptal.com/developers/gitignore/api/linux,rust,terraform
68 |
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Created by https://www.toptal.com/developers/gitignore/api/linux,rust,terraform
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=linux,rust,terraform
3 |
4 | ### Linux ###
5 | *~
6 |
7 | # temporary files which can be created if a process still has a handle open of a deleted file
8 | .fuse_hidden*
9 |
10 | # KDE directory preferences
11 | .directory
12 |
13 | # Linux trash folder which might appear on any partition or disk
14 | .Trash-*
15 |
16 | # .nfs files are created when an open file is removed but is still being accessed
17 | .nfs*
18 |
19 | ### Rust ###
20 | # Generated by Cargo
21 | # will have compiled files and executables
22 | debug/
23 | target/
24 |
25 | # These are backup files generated by rustfmt
26 | **/*.rs.bk
27 |
28 | # MSVC Windows builds of rustc generate these, which store debugging information
29 | *.pdb
30 |
31 | ### Terraform ###
32 | # Local .terraform directories
33 | **/.terraform/*
34 |
35 | # .tfstate files
36 | *.tfstate
37 | *.tfstate.*
38 |
39 | # Crash log files
40 | crash.log
41 | crash.*.log
42 |
43 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as
44 | # password, private keys, and other secrets. These should not be part of version
45 | # control as they are data points which are potentially sensitive and subject
46 | # to change depending on the environment.
47 | *.tfvars
48 | *.tfvars.json
49 |
50 | # Ignore override files as they are usually used to override resources locally and so
51 | # are not checked in
52 | override.tf
53 | override.tf.json
54 | *_override.tf
55 | *_override.tf.json
56 |
57 | # Include override files you do wish to add to version control using negated pattern
58 | # !example_override.tf
59 |
60 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
61 | # example: *tfplan*
62 |
63 | # Ignore CLI configuration files
64 | .terraformrc
65 | terraform.rc
66 |
67 | # Ignore .DS_Store files, which are created by macOS Finder
68 | .DS_Store
69 |
70 | # End of https://www.toptal.com/developers/gitignore/api/linux,rust,terraform
71 |
```
--------------------------------------------------------------------------------
/iac/.terraform.lock.hcl:
--------------------------------------------------------------------------------
```
1 | # This file is maintained automatically by "terraform init".
2 | # Manual edits may be lost in future updates.
3 |
4 | provider "registry.terraform.io/hashicorp/google" {
5 | version = "4.85.0"
6 | constraints = ">= 3.53.0, < 5.0.0"
7 | hashes = [
8 | "h1:aSRZcEKF2wOi/v24IA+k9J2Y7aKVV1cHi/R0V3EhxXQ=",
9 | "zh:17d60a6a6c1741cf1e09ac6731433a30950285eac88236e623ab4cbf23832ca3",
10 | "zh:1c70254c016439dbb75cab646b4beace6ceeff117c75d81f2cc27d41c312f752",
11 | "zh:35e2aa2cc7ac84ce55e05bb4de7b461b169d3582e56d3262e249ff09d64fe008",
12 | "zh:417afb08d7b2744429f6b76806f4134d62b0354acf98e8a6c00de3c24f2bb6ad",
13 | "zh:622165d09d21d9a922c86f1fc7177a400507f2a8c4a4513114407ae04da2dd29",
14 | "zh:7cdb8e39a8ea0939558d87d2cb6caceded9e21f21003d9e9f9ce648d5db0bc3a",
15 | "zh:851e737dc551d6004a860a8907fda65118fc2c7ede9fa828f7be704a2a39e68f",
16 | "zh:a331ad289a02a2c4473572a573dc389be0a604cdd9e03dd8dbc10297fb14f14d",
17 | "zh:b67fd531251380decd8dd1f849460d60f329f89df3d15f5815849a1dd001f430",
18 | "zh:be8785957acca4f97aa3e800b313b57d1fca07788761c8867c9bc701fbe0bdb5",
19 | "zh:cb6579a259fe020e1f88217d8f6937b2d5ace15b6406370977a1966eb31b1ca5",
20 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
21 | ]
22 | }
23 |
24 | provider "registry.terraform.io/hashicorp/google-beta" {
25 | version = "4.85.0"
26 | constraints = ">= 4.40.0, < 5.0.0"
27 | hashes = [
28 | "h1:YkCDGkP0AUZoNobLoxRnM52Pi4alYE9EFXalEu8p8E8=",
29 | "zh:40e9c7ec46955b4d79065a14185043a4ad6af8d0246715853fc5c99208b66980",
30 | "zh:5950a9ba2f96420ea5335b543e315b1a47a705f9a9abfc53c6fec52d084eddcb",
31 | "zh:5dfa98d32246a5d97e018f2b91b0e921cc6f061bc8591884f3b144f0d62f1c20",
32 | "zh:628d0ca35c6d4c35077859bb0a5534c1de44f23a91e190f9c3f06f2358172e75",
33 | "zh:6e78d54fd4de4151968149b4c3521f563a8b5c55aad423dba5968a9114b65ae4",
34 | "zh:91c3bc443188638353285bd35b06d3a3b39b42b3b4cc0637599a430438fba2f7",
35 | "zh:9e91b03363ebf39eea5ec0fbe7675f6979883aa9ad9a36664357d8513a007cf3",
36 | "zh:db9a8d6bfe075fb38c260986ab557d40e8d18e5698c62956a6da8120fae01d59",
37 | "zh:e41169c49f3bb53217905509e2ba8bb4680c373e1f54db7fac1b7f72943a1004",
38 | "zh:f32f55a8af605afbc940814e17493ac83d9d66cd6da9bbc247e0a833a0aa37ec",
39 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
40 | "zh:f6561a6badc3af842f9ad5bb926104954047f07cb90fadcca1357441cc67d91d",
41 | ]
42 | }
43 |
44 | provider "registry.terraform.io/hashicorp/random" {
45 | version = "3.7.1"
46 | constraints = ">= 2.1.0"
47 | hashes = [
48 | "h1:/qtweZW2sk0kBNiQM02RvBXmlVdI9oYqRMCyBZ8XA98=",
49 | "zh:3193b89b43bf5805493e290374cdda5132578de6535f8009547c8b5d7a351585",
50 | "zh:3218320de4be943e5812ed3de995946056db86eb8d03aa3f074e0c7316599bef",
51 | "zh:419861805a37fa443e7d63b69fb3279926ccf98a79d256c422d5d82f0f387d1d",
52 | "zh:4df9bd9d839b8fc11a3b8098a604b9b46e2235eb65ef15f4432bde0e175f9ca6",
53 | "zh:5814be3f9c9cc39d2955d6f083bae793050d75c572e70ca11ccceb5517ced6b1",
54 | "zh:63c6548a06de1231c8ee5570e42ca09c4b3db336578ded39b938f2156f06dd2e",
55 | "zh:697e434c6bdee0502cc3deb098263b8dcd63948e8a96d61722811628dce2eba1",
56 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
57 | "zh:a0b8e44927e6327852bbfdc9d408d802569367f1e22a95bcdd7181b1c3b07601",
58 | "zh:b7d3af018683ef22794eea9c218bc72d7c35a2b3ede9233b69653b3c782ee436",
59 | "zh:d63b911d618a6fe446c65bfc21e793a7663e934b2fef833d42d3ccd38dd8d68d",
60 | "zh:fa985cd0b11e6d651f47cff3055f0a9fd085ec190b6dbe99bf5448174434cdea",
61 | ]
62 | }
63 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/time/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # time
2 |
3 | src: https://github.com/dylibso/mcp.run-servlets/tree/main/servlets/time
4 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qr-code/README.md:
--------------------------------------------------------------------------------
```markdown
1 | qr_code
2 | =======
3 |
4 | Source: [mcp.run-servlets](https://github.com/dylibso/mcp.run-servlets/tree/main/servlets/qr-code)
5 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/hash/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # hash
2 |
3 | A hyper-mcp plugin that generates do all kind of hashes for you.
4 |
5 | Supported hashing tools:
6 |
7 | - [x] base64
8 | - [x] base32
9 | - [x] sha256
10 | - [x] sha512
11 | - [x] md5
12 | - [x] sha1
13 | - [x] sha224
14 | - [x] sha384
15 |
16 | ## What it does
17 |
18 | Takes input text and hash it.
19 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fs/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # fs
2 |
3 | An example plugin that implement filesystem operations.
4 |
5 | ## Usage
6 |
7 | ```json
8 | {
9 | "plugins": [
10 | {
11 | "name": "fs",
12 | "path": "oci://ghcr.io/tuananh/fs-plugin:latest",
13 | "runtime_config": {
14 | "allowed_paths": ["/tmp"]
15 | }
16 | }
17 | ]
18 | }
19 |
20 | ```
21 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/eval-py/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # eval_py
2 |
3 | An example of using [RustPython](https://github.com/RustPython/RustPython) to evaluate Python code.
4 |
5 |
6 | 
7 |
8 | ## Usage
9 |
10 | ```json
11 | {
12 | "plugins": [
13 | {
14 | "name": "eval_py",
15 | "path": "/home/anh/Code/hyper-mcp/examples/plugins/v1/eval-py/target/wasm32-wasip1/release/plugin.wasm"
16 | }
17 | ]
18 | }
19 | ```
20 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/arxiv/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # arxiv
2 |
3 | A plugin that let you search for papers on arXiv and download them.
4 |
5 | ## Usage
6 |
7 | Call with:
8 | ```json
9 | {
10 | "plugins": [
11 | // {},
12 | {
13 | "name": "arxiv",
14 | "path": "/home/anh/Code/hyper-mcp/examples/plugins/v1/arxiv/target/wasm32-wasip1/release/plugin.wasm",
15 | "runtime_config": {
16 | "allowed_hosts": ["export.arxiv.org", "arxiv.org"],
17 | "allowed_paths": ["/tmp"]
18 | }
19 | }
20 | ]
21 | }
22 |
23 | ```
24 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crypto-price/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # crypto_price
2 |
3 | ## Usage
4 |
5 | ```json
6 | {
7 | "plugins": [
8 | {
9 | "name": "crypto_price",
10 | "path": "oci://ghcr.io/tuananh/crypto-price-plugin:latest",
11 | "runtime_config": {
12 | "allowed_hosts": ["api.coingecko.com"]
13 | }
14 | }
15 | ]
16 | }
17 | ```
18 |
19 | ## Notes
20 |
21 | - HTTP request need to use `pdk.NewHTTPRequest`.
22 |
23 | ```go
24 | req := pdk.NewHTTPRequest(pdk.MethodGet, url)
25 | resp := req.Send()
26 | ```
27 |
28 | - We use `tinygo` for WASI support.
29 |
30 | - Need to export `_Call` as `call` to make it consistent. Same with `describe`.
31 |
32 | ```
33 | //export call
34 | func _Call() int32 {
35 | ```
36 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/serper/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # serper
2 |
3 | A plugin that performs Google web search using the [Serper](https://serper.dev) API and returns the raw JSON response for the given query string.
4 |
5 | ## Requirements
6 |
7 | - Set `SERPER_API_KEY` in your config to your Serper API key.
8 |
9 | ## Usage
10 |
11 | Call with:
12 | ```json
13 | {
14 | "plugins": [
15 | {
16 | "name": "serper",
17 | "path": "oci://ghcr.io/tuananh/serper-plugin:latest",
18 | "runtime_config": {
19 | "env_vars": {
20 | "SERPER_API_KEY": "<your-serper-api-key>"
21 | },
22 | "allowed_hosts": ["google.serper.dev"]
23 | }
24 | }
25 | ]
26 | }
27 | ```
28 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fetch/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # fetch
2 |
3 | src: https://github.com/dylibso/mcp.run-servlets/tree/main/servlets/fetch
4 |
5 |
6 | A servlet that fetches web pages and converts them to markdown.
7 |
8 | ## What it does
9 |
10 | Takes a URL, fetches the page content, strips out scripts and styles, and converts the HTML to markdown format.
11 |
12 | ## Usage
13 |
14 | Call with:
15 | ```json
16 | {
17 | "plugins": [
18 | // {},
19 | {
20 | "name": "fetch",
21 | "path": "oci://ghcr.io/tuananh/fetch-plugin:latest",
22 | "runtime_config": {
23 | "allowed_hosts": ["*"]
24 | }
25 | }
26 | ]
27 | }
28 |
29 | ```
30 |
31 | Returns the page content converted to markdown format.
32 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/github/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # github
2 |
3 | [src](https://github.com/dylibso/mcp.run-servlets/tree/main/servlets/github)
4 |
5 | You can interact with GitHub via various tools available in this plugin: branches, repo, gist, issues, files, etc...
6 |
7 | ## Usage
8 |
9 | ```json
10 | {
11 | "plugins": [
12 | {
13 | "name": "github",
14 | "path": "oci://ghcr.io/tuananh/github-plugin:latest",
15 | "runtime_config": {
16 | "allowed_hosts": [
17 | "api.github.com"
18 | ],
19 | "env_vars": {
20 | "api-key": "ghp_xxxx"
21 | }
22 | }
23 | }
24 | ]
25 | }
26 | ```
27 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/myip/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # myip
2 |
3 | A example `hyper-mcp` plugin that tell you your IP address, using Cloudflare.
4 |
5 | This is an example of how to use HTTP with `hyper-mcp`.
6 |
7 | To use this, you will need to update your config like this. Note the `allowed_host` in `runtime_config` because we're using Cloudflare for this.
8 |
9 | ```json
10 | {
11 | "plugins": [
12 | {
13 | "name": "time",
14 | "path": "/home/anh/Code/hyper-mcp/wasm/time.wasm"
15 | },
16 | {
17 | "name": "qr_code",
18 | "path": "oci://ghcr.io/tuananh/qrcode-plugin:latest"
19 | },
20 | {
21 | "name": "hash",
22 | "path": "oci://ghcr.io/tuananh/hash-plugin:latest"
23 | },
24 | {
25 | "name": "myip",
26 | "path": "oci://ghcr.io/tuananh/myip-plugin:latest",
27 | "runtime_config": {
28 | "allowed_hosts": ["1.1.1.1"]
29 | }
30 | }
31 | ]
32 | }
33 | ```
34 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/sqlite/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # sqlite
2 |
3 | A plugin that provide SQLite interactions for `hyper-mcp`.
4 |
5 | ## Usage
6 |
7 | Call with:
8 | ```json
9 | {
10 | "plugins": [
11 | // {},
12 | {
13 | "name": "sqlite",
14 | "path": "oci://ghcr.io/tuananh/sqlite-plugin",
15 | "runtime_config": {
16 | "allowed_paths": ["/tmp"],
17 | "env_vars": {
18 | "db_path": "/tmp/memory.db"
19 | }
20 | }
21 | }
22 | ]
23 | }
24 |
25 | ```
26 |
27 | ## How to build
28 |
29 | This plugin requires you to have [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) installed.
30 |
31 | ```sh
32 | export WASI_SDK_PATH=`<wasi-sdk-path>` # in my case, it's /opt/wasi-sdk
33 | export CC_wasm32_wasip1="${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot"
34 | cargo build --release --target wasm32-wasip1
35 | ```
36 |
37 | See [Dockerfile](./Dockerfile) for reference.
38 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/memory/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # memory
2 |
3 | A plugin that let you save & retrieve memory, backed by SQLite.
4 |
5 | ## Usage
6 |
7 | Call with:
8 | ```json
9 | {
10 | "plugins": [
11 | // {},
12 | {
13 | "name": "memory",
14 | "path": "/home/anh/Code/hyper-mcp/examples/plugins/v1/memory/target/wasm32-wasip1/release/plugin.wasm",
15 | "runtime_config": {
16 | "allowed_paths": ["/tmp"],
17 | "env_vars": {
18 | "db_path": "/tmp/memory.db"
19 | }
20 | }
21 | }
22 | ]
23 | }
24 |
25 | ```
26 |
27 | ## How to build
28 |
29 | This plugin requires you to have [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) installed.
30 |
31 | ```sh
32 | export WASI_SDK_PATH=`<wasi-sdk-path>` # in my case, it's /opt/wasi-sdk
33 | export CC_wasm32_wasip1="${WASI_SDK_PATH}/bin/clang --sysroot=${WASI_SDK_PATH}/share/wasi-sysroot"
34 | cargo build --release --target wasm32-wasip1
35 | ```
36 |
37 | See [Dockerfile](./Dockerfile) for reference.
38 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/maven/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # maven
2 |
3 | A plugin that fetches the dependencies of a Maven package (from Maven Central) given its group, artifact, and version.
4 |
5 | ## What it does
6 |
7 | Given a Maven package (groupId, artifactId, version), fetches its POM file from Maven Central and returns its dependencies as JSON.
8 |
9 | ## Usage
10 |
11 | Call with:
12 | ```json
13 | {
14 | "plugins": [
15 | {
16 | "name": "mvn_fetch_deps",
17 | "path": "oci://ghcr.io/tuananh/maven-plugin:latest",
18 | "runtime_config": {
19 | "allowed_hosts": ["repo1.maven.org"]
20 | }
21 | }
22 | ]
23 | }
24 | ```
25 |
26 | ### Example input
27 |
28 | ```json
29 | {
30 | "name": "mvn_fetch_deps",
31 | "arguments": {
32 | "group": "org.springframework.boot",
33 | "artifact": "spring-boot-starter-web",
34 | "version": "3.5.0"
35 | }
36 | }
37 | ```
38 |
39 | ### Example output
40 |
41 | ```json
42 | {
43 | "dependencies": [
44 | {
45 | "groupId": "org.springframework.boot",
46 | "artifactId": "spring-boot-starter",
47 | "version": "3.5.0",
48 | "scope": "compile"
49 | },
50 | {
51 | "groupId": "org.springframework.boot",
52 | "artifactId": "spring-boot-starter-json",
53 | "version": "3.5.0",
54 | "scope": "compile"
55 | }
56 | // ...
57 | ]
58 | }
59 | ```
60 |
61 | Return the list of dependencies of the given Maven package.
62 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # meme_generator
2 |
3 | A plugin for generating memes using predefined templates with text overlays.
4 |
5 | ## What it does
6 |
7 | Generates memes by overlaying customized text on predefined meme templates in classic meme style. The plugin supports various text styles, alignments, and positioning based on template configurations.
8 |
9 | ## Usage
10 |
11 | Call with:
12 | ```json
13 | {
14 | "plugins": [
15 | {
16 | "name": "meme_generator",
17 | "path": "oci://ghcr.io/tuananh/meme-generator-plugin:latest"
18 | }
19 | ]
20 | }
21 | ```
22 |
23 | The plugin provides the following tools:
24 |
25 | ### meme_list_templates
26 | Lists all available meme templates.
27 |
28 | ### meme_get_template
29 | Gets details about a specific meme template.
30 |
31 | Parameters:
32 | - `template_id`: The ID of the template to retrieve
33 |
34 | ### meme_generate
35 | Generates a meme using a template and custom text.
36 |
37 | Parameters:
38 | - `template_id`: The ID of the template to use
39 | - `texts`: Array of text strings to place on the meme according to the template configuration
40 |
41 | Each template can have specific configurations for:
42 | - Text positioning and alignment
43 | - Font scaling and style (uppercase/normal)
44 | - Text color
45 | - Multiple text overlays
46 |
47 | The generated output is a PNG image with the text overlaid on the template according to the specified configuration.
48 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gomodule/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # gomodule
2 |
3 | A plugin that fetches Go module information and latest versions from `proxy.golang.org`.
4 |
5 | ## What it does
6 |
7 | Provides two main functionalities:
8 | 1. `go_module_latest_version`: Fetches the latest version of multiple Go modules
9 | 2. `go_module_info`: Fetches detailed information about multiple Go modules
10 |
11 | ## Usage
12 |
13 | Call with:
14 | ```json
15 | {
16 | "plugins": [
17 | {
18 | "name": "gomodule",
19 | "path": "oci://ghcr.io/tuananh/gomodule-plugin:latest",
20 | "runtime_config": {
21 | "allowed_hosts": ["proxy.golang.org"]
22 | }
23 | }
24 | ]
25 | }
26 | ```
27 |
28 | ### Example Usage
29 |
30 | 1. Get latest version of multiple Go modules:
31 | ```json
32 | {
33 | "name": "go_module_latest_version",
34 | "params": {
35 | "module_names": "github.com/spf13/cobra,github.com/gorilla/mux,github.com/gin-gonic/gin"
36 | }
37 | }
38 | ```
39 |
40 | 2. Get detailed information about multiple Go modules:
41 | ```json
42 | {
43 | "name": "go_module_info",
44 | "params": {
45 | "module_names": "github.com/spf13/cobra,github.com/gorilla/mux,github.com/gin-gonic/gin"
46 | }
47 | }
48 | ```
49 |
50 | Returns:
51 | - For `go_module_latest_version`: A JSON object mapping module names to their latest version numbers
52 | - For `go_module_info`: An array of JSON objects containing detailed module information for each module, including:
53 | - Name
54 | - Latest version
55 | - Time
56 | - Version
57 | - And other metadata from proxy.golang.org
58 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # crates-io
2 |
3 | A plugin that fetches crate information and latest versions from crates.io.
4 |
5 | ## What it does
6 |
7 | Provides two main functionalities:
8 | 1. `crates_io_latest_version`: Fetches the latest version of multiple crates
9 | 2. `crates_io_crate_info`: Fetches detailed information about multiple crates including description, downloads, repository, documentation, etc.
10 |
11 | ## Usage
12 |
13 | Call with:
14 | ```json
15 | {
16 | "plugins": [
17 | {
18 | "name": "crates-io",
19 | "path": "oci://ghcr.io/tuananh/crates-io-plugin:latest",
20 | "runtime_config": {
21 | "allowed_hosts": ["crates.io"]
22 | }
23 | }
24 | ]
25 | }
26 | ```
27 |
28 | ### Example Usage
29 |
30 | 1. Get latest version of multiple crates:
31 | ```json
32 | {
33 | "name": "crates_io_latest_version",
34 | "params": {
35 | "crate_names": "serde,tokio,clap"
36 | }
37 | }
38 | ```
39 |
40 | 2. Get detailed information about multiple crates:
41 | ```json
42 | {
43 | "name": "crates_io_crate_info",
44 | "params": {
45 | "crate_names": "serde,tokio,clap"
46 | }
47 | }
48 | ```
49 |
50 | Returns:
51 | - For `crates_io_latest_version`: A JSON object mapping crate names to their latest version numbers
52 | - For `crates_io_crate_info`: An array of JSON objects containing detailed crate information for each crate, including:
53 | - Name
54 | - Description
55 | - Latest version
56 | - Download count
57 | - Repository URL
58 | - Documentation URL
59 | - Homepage URL
60 | - Keywords
61 | - Categories
62 | - License
63 | - Creation and update timestamps
64 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # qdrant
2 |
3 | A plugin that provides vector similarity search capabilities using Qdrant vector database.
4 |
5 | ## What it does
6 |
7 | This plugin provides three main functionalities:
8 | 1. Create collections with configurable vector dimensions
9 | 2. Store documents with their vector embeddings in Qdrant
10 | 3. Search for similar documents using vector embeddings
11 |
12 | ## Configuration
13 |
14 | The plugin requires the following configuration:
15 |
16 | ```json
17 | {
18 | "plugins": [
19 | {
20 | "name": "qdrant",
21 | "path": "oci://ghcr.io/tuananh/qdrant-plugin:latest",
22 | "runtime_config": {
23 | "QDRANT_URL": "http://localhost:6334",
24 | "allowed_hosts": [
25 | "localhost:6333"
26 | ],
27 | "env_vars": {
28 | "QDRANT_URL": "http://localhost:6333"
29 | }
30 | }
31 | }
32 | ]
33 | }
34 | ```
35 |
36 | ## Tools
37 |
38 | ### 1. qdrant_create_collection
39 |
40 | Creates a new collection in Qdrant with specified vector dimensions.
41 |
42 | ```json
43 | {
44 | "collection_name": "my_documents",
45 | "vector_size": 384 // Optional, defaults to 384
46 | }
47 | ```
48 |
49 | ### 2. qdrant_store
50 |
51 | Stores a document with its vector embedding in Qdrant.
52 |
53 | ```json
54 | {
55 | "collection_name": "my_documents",
56 | "text": "Your document text",
57 | "vector": [0.1, 0.2, ...] // Vector dimensions must match collection's vector_size
58 | }
59 | ```
60 |
61 | ### 3. qdrant_find
62 |
63 | Finds similar documents using vector similarity search.
64 |
65 | ```json
66 | {
67 | "collection_name": "my_documents",
68 | "vector": [0.1, 0.2, ...], // Vector dimensions must match collection's vector_size
69 | "limit": 5 // Optional, defaults to 5
70 | }
71 | ```
72 |
73 | ## Features
74 |
75 | - Configurable vector dimensions per collection
76 | - Support for vector-based queries
77 | - Configurable similarity search results limit
78 | - Uses cosine similarity for vector matching
79 | - Thread-safe operations
80 |
81 | ## Dependencies
82 |
83 | - Qdrant for vector storage and similarity search
84 | - UUID for document identification
85 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/think/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # think
2 |
3 | A simple MCP plugin that returns the provided thought string. Useful for agentic reasoning, cache memory, or when you want to "think out loud" in a workflow.
4 |
5 | ## What it does
6 |
7 | Takes a `thought` parameter (string) and simply returns it as the result. No side effects, no logging, no database or network calls.
8 |
9 | Read more about the think tool in [this blog post](https://www.anthropic.com/engineering/claude-think-tool).
10 |
11 | ## Usage
12 |
13 | Call with:
14 | ```json
15 | {
16 | "plugins": [
17 | {
18 | "name": "think",
19 | "path": "oci://ghcr.io/tuananh/think-plugin:latest"
20 | }
21 | ]
22 | }
23 | ```
24 |
25 | ### Example
26 |
27 | Tool call:
28 | ```json
29 | {
30 | "name": "think",
31 | "arguments": { "thought": "I should try a different approach." }
32 | }
33 | ```
34 | Returns:
35 | ```json
36 | "I should try a different approach."
37 | ```
38 |
39 | ## Example usage with Cursor/Windsurf
40 |
41 | Add a new Cursor/Windsurf rule like the following
42 |
43 | ```
44 | After any context change (viewing new files, running commands, or receiving tool outputs), use the "think" tool to organize your reasoning before responding.
45 |
46 | Specifically, always use the think tool when:
47 | - After examining file contents or project structure
48 | - After running terminal commands or analyzing their outputs
49 | - After receiving search results or API responses
50 | - Before making code suggestions or explaining complex concepts
51 | - When transitioning between different parts of a task
52 |
53 | When using the think tool:
54 | - List the specific rules or constraints that apply to the current task
55 | - Check if all required information is collected
56 | - Verify that your planned approach is correct
57 | - Break down complex problems into clearly defined steps
58 | - Analyze outputs from other tools thoroughly
59 | - Plan multi-step approaches before executing them
60 |
61 | The think tool has been proven to improve performance by up to 54% on complex tasks, especially when working with multiple tools or following detailed policies.
62 | ```
63 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/context7/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Context7 API Tools Plugin
2 |
3 | This plugin provides tools to interact with the Context7 API, allowing for resolving library IDs and fetching documentation.
4 |
5 | ## Usage
6 |
7 | ```
8 | {
9 | "plugins": [
10 | {
11 | "name": "context7",
12 | "path": "oci://ghcr.io/tuananh/context7-plugin:nightly",
13 | "runtime_config": {
14 | "allowed_hosts": ["context7.com"]
15 | }
16 | }
17 | ]
18 | }
19 | ```
20 |
21 | ## Tools
22 |
23 | ### 1. `c7_resolve_library_id`
24 |
25 | **Description:** Resolves a package name to a Context7-compatible library ID and returns a list of matching libraries. You MUST call this function before 'c7_get_library_docs' to obtain a valid Context7-compatible library ID. When selecting the best match, consider: - Name similarity to the query - Description relevance - Code Snippet count (documentation coverage) - GitHub Stars (popularity) Return the selected library ID and explain your choice. If there are multiple good matches, mention this but proceed with the most relevant one.
26 |
27 | **Input Schema:**
28 | An object with the following properties:
29 | - `library_name` (string, required): The general name of the library (e.g., 'React', 'upstash/redis').
30 |
31 | **Example Input:**
32 | ```json
33 | {
34 | "library_name": "upstash/redis"
35 | }
36 | ```
37 |
38 | **Output:**
39 | A JSON string containing the resolved Context7 compatible library ID.
40 |
41 | **Example Output:**
42 | ```json
43 | {
44 | "context7_compatible_library_id": "upstash_redis_id"
45 | }
46 | ```
47 |
48 | ### 2. `c7_get_library_docs`
49 |
50 | **Description:** Fetches up-to-date documentation for a library. You must call 'c7_resolve_library_id' first to obtain the exact Context7-compatible library ID required to use this tool.
51 |
52 | **Input Schema:**
53 | An object with the following properties:
54 | - `context7_compatible_library_id` (string, required): The Context7-compatible ID for the library.
55 | - `topic` (string, optional): Focus the docs on a specific topic (e.g., 'routing', 'hooks').
56 | - `tokens` (integer, optional): Max number of tokens for the documentation (default: 10000).
57 |
58 | **Example Input:**
59 | ```json
60 | {
61 | "context7_compatible_library_id": "upstash_redis_id",
62 | "topic": "data_types",
63 | "tokens": 5000
64 | }
65 | ```
66 |
67 | **Output:**
68 |
69 | The fetched documentation in text format.
70 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gitlab/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # gitlab
2 |
3 | A plugin that implements GitLab operations including issue management, file handling, branch management, and snippet operations.
4 |
5 | ## Configuration
6 |
7 | The plugin requires the following configuration:
8 |
9 | - `GITLAB_TOKEN`: (Required) Your GitLab personal access token
10 | - `GITLAB_URL`: (Optional) Your GitLab instance URL. Defaults to `https://gitlab.com/api/v4`
11 |
12 | ## Usage
13 |
14 | ```json
15 | {
16 | "plugins": [
17 | {
18 | "name": "gitlab",
19 | "path": "oci://ghcr.io/tuananh/gitlab-plugin:latest",
20 | "runtime_config": {
21 | "allowed_hosts": ["gitlab.com"], // Your GitLab host
22 | "env_vars": {
23 | "GITLAB_TOKEN": "your-gitlab-token",
24 | "GITLAB_URL": "https://gitlab.com/api/v4" // Optional, defaults to GitLab.com
25 | }
26 | }
27 | }
28 | ]
29 | }
30 | ```
31 |
32 | ## Available Operations
33 |
34 | ### Issues
35 | - [x] `gl_create_issue`: Create a new issue
36 | - [x] `gl_get_issue`: Get issue details
37 | - [x] `gl_update_issue`: Update an existing issue
38 | - [x] `gl_add_issue_comment`: Add a comment to an issue
39 | - [x] `gl_list_issues`: List issues for a project in GitLab. Supports filtering by state and labels.
40 |
41 | ### Files
42 | - [x] `gl_get_file_contents`: Get file contents
43 | - [x] `gl_create_or_update_file`: Create or update a file
44 | - [x] `gl_delete_file`: Delete a file from the repository
45 | - [ ] `gl_push_files`: Push multiple files
46 |
47 | ### Branches and Merge Requests
48 | - [x] `gl_create_branch`: Create a new branch
49 | - [x] `gl_list_branches`: List all branches in a GitLab project
50 | - [x] `gl_create_merge_request`: Create a merge request
51 | - [x] `gl_update_merge_request`: Update an existing merge request in a GitLab project.
52 | - [x] `gl_get_merge_request`: Get details of a specific merge request in a GitLab project.
53 |
54 | ### Snippets
55 | - [x] `gl_create_snippet`: Create a new snippet
56 | - [x] `gl_update_snippet`: Update an existing snippet
57 | - [x] `gl_get_snippet`: Get snippet details
58 | - [x] `gl_delete_snippet`: Delete a snippet
59 |
60 | ### Repository
61 | - [x] `gl_get_repo_tree`: Get the list of files and directories in a project repository. Handles pagination internally.
62 | - [x] `gl_get_repo_members`: Get a list of members for a GitLab project. Supports fetching direct or inherited members and filtering by query. Handles pagination internally.
63 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/tool-list-changed/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Tool List Changed Plugin
2 |
3 | This plugin demonstrates dynamic tool list management in hyper-mcp. It showcases how a plugin can modify its tool list at runtime and notify the MCP server about these changes.
4 |
5 | ## Features
6 |
7 | - **Dynamic Tool Creation**: Starts with a single `add_tool` and dynamically creates new tools
8 | - **Host Function Integration**: Uses the `notify_tool_list_changed` host function to notify the server
9 | - **Atomic Counter**: Thread-safe tool counting using atomic operations
10 |
11 | ## How It Works
12 |
13 | The plugin begins with only one callable tool:
14 |
15 | - `add_tool`: When called, this tool creates a new tool named `tool_n` (where n starts at 1 and increments)
16 |
17 | After each call to `add_tool`:
18 | 1. A new tool `tool_n` is added to the plugin's tool list
19 | 2. The plugin calls `notify_tool_list_changed()` to inform the MCP server
20 | 3. The server updates its understanding of available tools
21 |
22 | ## Tools
23 |
24 | ### Initial Tool
25 |
26 | - **add_tool**: Creates a new dynamic tool and notifies the server of the tool list change
27 | - Takes no parameters
28 | - Returns a JSON object with the new tool name and current tool count
29 |
30 | ### Dynamic Tools
31 |
32 | - **tool_1, tool_2, tool_3, ...**: Created dynamically when `add_tool` is called
33 | - Each tool returns information about itself when called
34 | - Takes no parameters
35 |
36 | ## Usage Example
37 |
38 | 1. **Initial state**: Only `add_tool` is available
39 | 2. **Call `add_tool`**: Creates `tool_1` and notifies the server
40 | 3. **Call `add_tool` again**: Creates `tool_2` and notifies the server
41 | 4. **Call `tool_1`**: Returns information about being the first dynamically created tool
42 |
43 | ## Building
44 |
45 | ```bash
46 | cd hyper-mcp/examples/plugins/v1/tool-list-changed
47 | cargo build --target wasm32-unknown-unknown --release
48 | ```
49 |
50 | The compiled WASM file will be available at:
51 | `target/wasm32-unknown-unknown/release/tool_list_changed.wasm`
52 |
53 | ## Configuration
54 |
55 | This plugin requires no additional configuration. It uses atomic operations to maintain thread-safe state across calls.
56 |
57 | ## Implementation Details
58 |
59 | - Uses `AtomicUsize` for thread-safe tool counting
60 | - Calls the `notify_tool_list_changed` host function after each tool addition
61 | - Implements both static (`add_tool`) and dynamic (`tool_n`) tool handling
62 | - Provides JSON responses with relevant information about operations
63 |
```
--------------------------------------------------------------------------------
/examples/plugins/v2/rstime/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # rstime Plugin
2 |
3 | A Model Context Protocol (MCP) plugin for working with time and timezone information. The `rstime` plugin provides tools for retrieving the current time in different timezones and parsing RFC2822 formatted time strings.
4 |
5 | ## Overview
6 |
7 | The rstime plugin is a WebAssembly-based MCP plugin written in Rust that exposes time-related functionality through the Model Context Protocol. It allows LLM clients to:
8 |
9 | - Get the current time in any timezone
10 | - Parse RFC2822 formatted time strings to Unix timestamps
11 | - Complete timezone names for better user experience
12 |
13 | ## Features
14 |
15 | ### Tools
16 |
17 | #### `get_time`
18 | Returns the current time in a specified timezone.
19 |
20 | **Input:**
21 | - `timezone` (optional, string): The timezone identifier (e.g., `America/New_York`, `Europe/London`, `Asia/Tokyo`). Defaults to `UTC` if not provided.
22 |
23 | **Output:**
24 | - `current_time` (string): The current time in RFC2822 format for the specified timezone.
25 |
26 | **Example:**
27 | ```
28 | Tool: get_time
29 | Input: {"timezone": "America/Los_Angeles"}
30 | Output: "Tue, 15 Jan 2024 10:30:45 -0800"
31 | ```
32 |
33 | #### `parse_time`
34 | Parses an RFC2822 formatted time string and returns the corresponding Unix timestamp.
35 |
36 | **Input:**
37 | - `time` (required, string): The time string in RFC2822 format to parse.
38 |
39 | **Output:**
40 | - `timestamp` (integer): The parsed timestamp in seconds since the Unix epoch.
41 |
42 | **Example:**
43 | ```
44 | Tool: parse_time
45 | Input: {"time": "Tue, 15 Jan 2024 18:30:45 +0000"}
46 | Output: 1705344645
47 | ```
48 |
49 | ### Prompts
50 |
51 | #### `get_time_with_timezone`
52 | A prompt that guides the user to get the current time for a specific timezone.
53 |
54 | **Arguments:**
55 | - `timezone` (optional, string): The timezone to retrieve time information for. Defaults to `UTC`.
56 |
57 | **Response:**
58 | Returns an assistant message suggesting to get the time for the specified timezone.
59 |
60 | ## Building
61 |
62 | ### Prerequisites
63 | - Rust 1.70 or later
64 | - Extism toolchain for WASM compilation
65 |
66 | ### Build Steps
67 |
68 | ```bash
69 | # Build the WebAssembly plugin
70 | cargo build --release --target wasm32-wasip1
71 |
72 | # The compiled WASM file will be available at target/wasm32-wasip1/release/plugin.wasm
73 | ```
74 |
75 | Alternatively, you can use the provided `prepare.sh` script:
76 |
77 | ```bash
78 | ./prepare.sh
79 | ```
80 |
81 | This will create the compiled `rstime.wasm` file.
82 |
83 | ## Docker Support
84 |
85 | A Dockerfile is included for containerized deployment. Build it with:
86 |
87 | ```bash
88 | docker build -t rstime:latest .
89 | ```
90 |
91 | ## Testing
92 |
93 | The plugin includes comprehensive test coverage. Run tests with, changing the target to your architecture:
94 |
95 | ```bash
96 | cargo test --lib --target x86_64-apple-darwin
97 | ```
98 |
99 | ### Test Coverage
100 |
101 | The test suite covers:
102 | - Getting time in UTC and various timezones
103 | - Handling invalid timezone names
104 | - Parsing valid and invalid RFC2822 time strings
105 | - Tool listing and metadata
106 | - Prompt retrieval and listing
107 | - Resource operations
108 | - Error handling and edge cases
109 |
110 | ## Supported Timezones
111 |
112 | The plugin supports all timezones from the IANA timezone database through the `chrono-tz` crate. Some common examples include:
113 |
114 | - `UTC` - Coordinated Universal Time
115 | - `America/New_York` - Eastern Time
116 | - `America/Chicago` - Central Time
117 | - `America/Denver` - Mountain Time
118 | - `America/Los_Angeles` - Pacific Time
119 | - `Europe/London` - Greenwich Mean Time
120 | - `Europe/Paris` - Central European Time
121 | - `Asia/Tokyo` - Japan Standard Time
122 | - `Asia/Shanghai` - China Standard Time
123 | - `Australia/Sydney` - Australian Eastern Time
124 |
125 | For a complete list, refer to the IANA timezone database.
126 |
127 | ## Dependencies
128 |
129 | - **chrono** - Date and time handling
130 | - **chrono-tz** - Timezone support
131 | - **extism-pdk** - Extism Plugin Development Kit for MCP
132 | - **serde** - Serialization/deserialization
133 | - **serde_json** - JSON handling
134 | - **anyhow** - Error handling
135 | - **base64** - Base64 encoding/decoding
136 |
137 | ## Usage Example
138 |
139 | When integrated with an MCP-compatible client, you can use the plugin like this:
140 |
141 | ```
142 | User: What time is it in Tokyo?
143 |
144 | Client calls: get_time tool with {"timezone": "Asia/Tokyo"}
145 | Plugin returns: Current time in RFC2822 format for Asia/Tokyo
146 |
147 | Client: The current time in Tokyo is [result]
148 | ```
149 |
150 | ## Architecture
151 |
152 | The plugin follows the Extism PDK architecture:
153 |
154 | - **lib.rs**: Main plugin implementation with tool and prompt logic
155 | - **pdk**: PDK-specific types and exported functions for MCP communication
156 | - **Cargo.toml**: Rust dependencies and build configuration
157 |
158 | The plugin compiles to WebAssembly and implements the MCP protocol through Extism's foreign function interface.
159 |
160 | ## Error Handling
161 |
162 | The plugin gracefully handles common errors:
163 |
164 | - **Invalid timezone**: Returns a descriptive error message
165 | - **Missing required arguments**: Provides helpful feedback about required parameters
166 | - **Invalid time format**: Reports parsing errors with context
167 | - **Unknown tools/prompts**: Returns appropriate error responses
168 |
169 | ## Contributing
170 |
171 | When contributing to this plugin:
172 |
173 | 1. Maintain the existing code style
174 | 2. Add tests for new functionality
175 | 3. Update this README with any new features
176 | 4. Ensure all tests pass before submitting
177 |
178 | ## License
179 |
180 | This plugin is part of the hyper-mcp project. See the main project repository for license information.
181 |
```
--------------------------------------------------------------------------------
/templates/plugins/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Plugin Templates
2 |
3 | This directory contains templates for creating plugins for hyper-mcp in various programming languages. Plugins extend hyper-mcp's functionality by providing tools, resources, prompts, and other MCP capabilities through WebAssembly modules.
4 |
5 | ## Available Templates
6 |
7 | ### 🦀 Rust
8 |
9 | The recommended language for building hyper-mcp plugins. Rust provides excellent performance, safety, and tooling for WebAssembly development.
10 |
11 | - **Location**: `rust/`
12 | - **Getting Started**: See [rust/README.md](./rust/README.md)
13 | - **Use When**: You want the best performance, safety, and ecosystem support
14 | - **Compile Target**: WebAssembly (`wasm32-wasip1`)
15 |
16 | **Key Features:**
17 | - Excellent WASM performance and code size
18 | - Strong type system catches errors at compile time
19 | - Rich ecosystem of crates for common tasks
20 | - Memory-safe execution model
21 | - Direct support for Extism PDK
22 |
23 | ### 🐹 Go
24 |
25 | A modern, approachable language for building hyper-mcp plugins. Go offers simplicity, fast compilation, and a clean standard library for WebAssembly development.
26 |
27 | - **Location**: `go/`
28 | - **Getting Started**: See [go/README.md](./go/README.md)
29 | - **Use When**: You prefer simplicity, fast development cycles, and Go's syntax
30 | - **Compile Target**: WebAssembly (`wasip1`)
31 |
32 | **Key Features:**
33 | - Simple, readable syntax with fast learning curve
34 | - Fast compilation times
35 | - Excellent standard library
36 | - Strong concurrency primitives (though limited in WASM)
37 | - Growing WASM ecosystem with Extism Go PDK support
38 |
39 | ## Quick Start
40 |
41 | 1. **Choose a template language** (Rust or Go)
42 | 2. **Read the template README** for language-specific setup instructions
43 | 3. **Implement your plugin** by adding tools, resources, prompts, etc.
44 | 4. **Build and test locally** using the provided build instructions
45 | 5. **Publish to a registry** following the distribution guide
46 |
47 | ## Plugin Capabilities
48 |
49 | Plugins can provide any combination of:
50 |
51 | - **Tools** - Functions that clients can call with structured inputs
52 | - **Resources** - URI-based references to files, data, or services
53 | - **Resource Templates** - URI patterns for dynamic resource discovery
54 | - **Prompts** - Pre-defined prompts for specific use cases
55 | - **Completions** - Auto-completion suggestions for user input
56 |
57 | ## Plugin Development Workflow
58 |
59 | ```
60 | 1. Create project from template
61 | ↓
62 | 2. Implement plugin handlers
63 | ↓
64 | 3. Build to WebAssembly
65 | ↓
66 | 4. Test locally with hyper-mcp
67 | ↓
68 | 5. Build Docker image
69 | ↓
70 | 6. Push to registry (Docker Hub, GHCR, etc.)
71 | ↓
72 | 7. Configure in hyper-mcp's config.json
73 | ↓
74 | 8. Use in Claude Desktop, Cursor IDE, or other MCP clients
75 | ```
76 |
77 | ## Common Tasks
78 |
79 | ### Set Up Development Environment
80 |
81 | Follow the language-specific template README (e.g., [rust/README.md](./rust/README.md) or [go/README.md](./go/README.md)) for:
82 | - Required tools and dependencies
83 | - Target/runtime setup
84 | - Local build instructions
85 |
86 | ### Implement Plugin Handlers
87 |
88 | **Only implement what you need** - for example:
89 | - Tools-only plugin: `list_tools()` + `call_tool()`
90 | - Resources-only plugin: `list_resources()` + `read_resource()`
91 | - Prompts-only plugin: `list_prompts()` + `get_prompt()`
92 |
93 | See the template README for a complete handler reference table.
94 |
95 | ### Call Host Functions
96 |
97 | Your plugin can call host functions to interact with the MCP client:
98 | - Request user input with `create_elicitation()`
99 | - Generate messages with `create_message()`
100 | - Report progress with `notify_progress()`
101 | - Send logs with `notify_logging_message()`
102 | - Query available roots with `list_roots()`
103 | - Notify about changes to tools, resources, or prompts
104 |
105 | See the template README for complete host function documentation.
106 |
107 | ### Build for Production
108 |
109 | Each template includes a `Dockerfile` for reproducible, multi-stage builds:
110 |
111 | ```bash
112 | docker build -t your-registry/your-plugin-name .
113 | docker push your-registry/your-plugin-name:latest
114 | ```
115 |
116 | ### Configure in hyper-mcp
117 |
118 | Add your plugin to hyper-mcp's config file:
119 |
120 | ```json
121 | {
122 | "plugins": {
123 | "my_plugin": {
124 | "url": "oci://your-registry/your-plugin-name:latest"
125 | }
126 | }
127 | }
128 | ```
129 |
130 | For local development, use a file:// URL:
131 |
132 | ```json
133 | {
134 | "plugins": {
135 | "my_plugin": {
136 | "url": "file:///path/to/plugin.wasm"
137 | }
138 | }
139 | }
140 | ```
141 |
142 | ## Language Comparison
143 |
144 | | Feature | Rust | Go |
145 | |---------|------|-----|
146 | | Performance | Excellent | Very Good |
147 | | Code Size | Small | Medium |
148 | | Learning Curve | Steep | Gentle |
149 | | Compilation Speed | Moderate | Fast |
150 | | Type Safety | Very Strong | Good |
151 | | Standard Library | Moderate | Excellent |
152 | | WASM Support | Native | Excellent (1.22+) |
153 | | Extism Support | Full | Full |
154 |
155 | ## Resources
156 |
157 | - [hyper-mcp Main README](https://github.com/tuananh/hyper-mcp#readme)
158 | - [hyper-mcp Plugin Creation Guide](https://github.com/tuananh/hyper-mcp/blob/main/CREATING_PLUGINS.md)
159 | - [MCP Protocol Specification](https://spec.modelcontextprotocol.io/)
160 | - [Extism Documentation](https://docs.extism.org/)
161 | - [Extism Go PDK](https://github.com/extism/go-pdk)
162 | - [Example Plugins](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins)
163 |
164 | ## Adding More Templates
165 |
166 | To add a template for another language:
167 |
168 | 1. Create a new directory: `templates/plugins/your-language/`
169 | 2. Set up the build system for compiling to WebAssembly
170 | 3. Create a `Dockerfile` for building OCI container images
171 | 4. Add a comprehensive `README.md` following the pattern from existing templates
172 | 5. Include example implementations of key handlers
173 | 6. Convert all MCP protocol types from the Rust `types.rs` to your language
174 | 7. Submit as a contribution to hyper-mcp
175 |
176 | ## Support
177 |
178 | - Check the template README for language-specific questions
179 | - See [CREATING_PLUGINS.md](../CREATING_PLUGINS.md) for general plugin development
180 | - Review [example plugins](../examples/plugins/) for working implementations
181 | - Open an issue on [GitHub](https://github.com/tuananh/hyper-mcp) for bugs or feature requests
182 |
183 | Happy plugin building! 🚀
184 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | <div align="center">
2 | <picture>
3 | <img alt="hyper-mcp logo" src="./assets/logo.png" width="50%">
4 | </picture>
5 | </div>
6 |
7 | <div align="center">
8 |
9 | [](https://crates.io/crates/hyper-mcp)
10 | [](#license)
11 | [](https://github.com/tuananh/hyper-mcp/issues)
12 | 
13 |
14 | <a href="https://trendshift.io/repositories/13451" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13451" alt="tuananh%2Fhyper-mcp | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
15 |
16 | </div>
17 |
18 | # hyper-mcp
19 |
20 | A fast, secure MCP server that extends its capabilities through WebAssembly plugins.
21 |
22 | ## What is it?
23 |
24 | hyper-mcp makes it easy to add AI capabilities to your applications. It works with Claude Desktop, Cursor IDE, and other MCP-compatible apps. Write plugins in your favorite language, distribute them through container registries, and run them anywhere - from cloud to edge.
25 |
26 | ## Features
27 |
28 | - Write plugins in any language that compiles to WebAssembly
29 | - Distribute plugins via standard OCI registries (like Docker Hub)
30 | - Built on [Extism](https://github.com/extism/extism) for rock-solid plugin support
31 | - Sanboxing with WASM: ability to limit network, filesystem, memory access
32 | - Lightweight enough for resource-constrained environments
33 | - Support all 3 protocols in the spec: `stdio`, `sse` and `streamble-http`.
34 | - Deploy anywhere: serverless, edge, mobile, IoT devices
35 | - Cross-platform compatibility out of the box
36 | - Support tool name prefix to prevent tool names collision
37 |
38 | ## Security
39 |
40 | Built with security-first mindset:
41 |
42 | - Sandboxed plugins that can't access your system without permission
43 | - Memory-safe execution with resource limits
44 | - Secure plugin distribution through container registries
45 | - Fine-grained access control for host functions
46 | - OCI plugin images are signed at publish time and verified at load time with [sigstore](https://www.sigstore.dev/).
47 |
48 | ## Getting Started
49 |
50 | 1. Create your config file:
51 | - Linux: `$HOME/.config/hyper-mcp/config.json`
52 | - Windows: `{FOLDERID_RoamingAppData}`. Eg: `C:\Users\Alice\AppData\Roaming`
53 | - macOS: `$HOME/Library/Application Support/hyper-mcp/config.json`
54 |
55 | ```json
56 | {
57 | "plugins": {
58 | "time": {
59 | "url": "oci://ghcr.io/tuananh/time-plugin:latest"
60 | },
61 | "qr_code": {
62 | "url": "oci://ghcr.io/tuananh/qrcode-plugin:latest"
63 | },
64 | "hash": {
65 | "url": "oci://ghcr.io/tuananh/hash-plugin:latest"
66 | },
67 | "myip": {
68 | "url": "oci://ghcr.io/tuananh/myip-plugin:latest",
69 | "runtime_config": {
70 | "allowed_hosts": ["1.1.1.1"]
71 | }
72 | },
73 | "fetch": {
74 | "url": "oci://ghcr.io/tuananh/fetch-plugin:latest",
75 | "runtime_config": {
76 | "allowed_hosts": ["*"],
77 | "memory_limit": "100 MB",
78 | }
79 | }
80 | }
81 | }
82 | ```
83 |
84 | > 📖 **For detailed configuration options including authentication setup, runtime configuration, and advanced features, see [RUNTIME_CONFIG.md](./RUNTIME_CONFIG.md)**
85 |
86 | Supported URL schemes:
87 | - `oci://` - for OCI-compliant registries (like Docker Hub, GitHub Container Registry, etc.)
88 | - `file://` - for local files
89 | - `http://` or `https://` - for remote files
90 | - `s3://` - for Amazon S3 objects (requires that you have your AWS credentials set up in the environment)
91 |
92 | 2. Start the server:
93 |
94 | ```sh
95 | $ hyper-mcp
96 | ```
97 |
98 | - By default, it will use `stdio` transport. If you want to use SSE, use flag `--transport sse` or streamable HTTP with `--transport streamable-http`.
99 | - If you want to debug, use `RUST_LOG=info`.
100 | - If you're loading unsigned OCI plugin, you need to set `insecure_skip_signature` flag or env var `HYPER_MCP_INSECURE_SKIP_SIGNATURE` to `true`
101 |
102 | ## Using with Cursor IDE
103 |
104 | You can configure hyper-mcp either globally for all projects or specifically for individual projects.
105 |
106 | 1. For project-scope configuration, create `.cursor/mcp.json` in your project root:
107 | ```json
108 | {
109 | "mcpServers": {
110 | "hyper-mcp": {
111 | "command": "/path/to/hyper-mcp"
112 | }
113 | }
114 | }
115 | ```
116 |
117 | 2. Set up hyper-mcp in Cursor's settings:
118 | 
119 |
120 | 3. Start using tools through chat:
121 | 
122 |
123 | ## Available Plugins
124 |
125 | We maintain several example plugins to get you started:
126 |
127 | ### V1 Plugins
128 |
129 | These plugins use the v1 plugin interface. While still supported, new plugins should use the v2 interface.
130 |
131 | - [time](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/time): Get current time and do time calculations (Rust)
132 | - [qr_code](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/qr-code): Generate QR codes (Rust)
133 | - [hash](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/hash): Generate various types of hashes (Rust)
134 | - [myip](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/myip): Get your current IP (Rust)
135 | - [fetch](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/fetch): Basic webpage fetching (Rust)
136 | - [crypto_price](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/crypto-price): Get cryptocurrency prices (Go)
137 | - [fs](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/fs): File system operations (Rust)
138 | - [github](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/github): GitHub plugin (Go)
139 | - [eval_py](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/eval-py): Evaluate Python code with RustPython (Rust)
140 | - [arxiv](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/arxiv): Search & download arXiv papers (Rust)
141 | - [memory](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/memory): Let you store & retrieve memory, powered by SQLite (Rust)
142 | - [sqlite](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/sqlite): Interact with SQLite (Rust)
143 | - [crates-io](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/crates-io): Get crate general information, check crate latest version (Rust)
144 | - [gomodule](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/gomodule): Get Go modules info, version (Rust)
145 | - [qdrant](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/qdrant): keeping & retrieving memories to Qdrant vector search engine (Rust)
146 | - [gitlab](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/gitlab): GitLab plugin (Rust)
147 | - [meme_generator](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/meme-generator): Meme generator (Rust)
148 | - [context7](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/context7): Lookup library documentation (Rust)
149 | - [think](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/think): Think tool(Rust)
150 | - [maven](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/maven): Maven plugin (Rust)
151 | - [serper](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v1/serper): Serper web search plugin (Rust)
152 |
153 | ### V2 Plugins
154 | These plugins use the v2 plugin interface. New plugins should use this interface.
155 |
156 | - [rstime](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins/v2/rstime): Get current time and do time calculations (Rust)
157 |
158 |
159 | ### Community-built plugins
160 |
161 | - [hackernews](https://github.com/hungran/hyper-mcp-hackernews-tool): This plugin connects to the Hacker News API to fetch the current top stories and display them with their titles, scores, authors, and URLs.
162 | - [release-monitor-id](https://github.com/ntheanh201/hyper-mcp-release-monitor-id-tool): This plugin retrieves project ID from release-monitoring.org, which helps track versions of released software.
163 | - [yahoo-finance](https://github.com/phamngocquy/hyper-mcp-yfinance): This plugin connects to the Yahoo Finance API to provide stock prices (OHLCV) based on a company name or ticker symbol.
164 | - [rand16](https://github.com/dabevlohn/rand16): This plugen generates random 16 bytes buffer and provides it in base64uri format - very usable for symmetric cryptography online.
165 |
166 | ## Documentation
167 |
168 | - **[Runtime Configuration Guide](./RUNTIME_CONFIG.md)** - Comprehensive guide to configuration options including:
169 | - Authentication setup (Basic, Token, and Keyring)
170 | - Plugin runtime configuration
171 | - Security considerations and best practices
172 | - Platform-specific keyring setup for macOS, Linux, and Windows
173 | - Troubleshooting authentication issues
174 | - **[Skip Tools Pattern Guide](./SKIP_TOOLS_GUIDE.md)** - Comprehensive guide to filtering tools using regex patterns:
175 | - Pattern syntax and examples
176 | - Common use cases and best practices
177 | - Environment-specific filtering strategies
178 | - Advanced regex techniques
179 | - Migration and troubleshooting
180 |
181 | ## Creating Plugins
182 |
183 | For comprehensive instructions on creating plugins, see [CREATING_PLUGINS.md](./CREATING_PLUGINS.md).
184 |
185 | ## License
186 |
187 | [Apache 2.0](./LICENSE)
188 |
189 | ## Star History
190 |
191 | [](https://www.star-history.com/#tuananh/hyper-mcp&Date)
192 |
```
--------------------------------------------------------------------------------
/templates/plugins/rust/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Rust Plugin Template
2 |
3 | A WebAssembly plugin template for building MCP (Model Context Protocol) plugins in Rust using the hyper-mcp framework.
4 |
5 | ## Overview
6 |
7 | This template provides a starter project for creating MCP plugins that run as WebAssembly modules. It includes all necessary dependencies and boilerplate code to implement MCP protocol handlers.
8 |
9 | ## Project Structure
10 |
11 | ```
12 | .
13 | ├── src/
14 | │ ├── lib.rs # Main plugin implementation
15 | │ └── pdk/ # Plugin Development Kit types and utilities
16 | ├── Cargo.toml # Rust dependencies and project metadata
17 | ├── Dockerfile # Multi-stage build for compiling to WASM
18 | └── .cargo/ # Cargo configuration
19 | ```
20 |
21 | ## Getting Started
22 |
23 | ### Prerequisites
24 |
25 | - Rust 1.88 or later
26 | - `wasm32-wasip1` target installed:
27 | ```sh
28 | rustup target add wasm32-wasip1
29 | ```
30 |
31 | ### Development
32 |
33 | 1. **Clone or use this template** to start your plugin project
34 |
35 | 2. **Implement plugin handlers** in `src/lib.rs`:
36 |
37 | > **Note:** You only need to implement the handlers relevant to your plugin. For example, if your plugin only provides tools, implement only `list_tools()` and `call_tool()`. All other handlers have default implementations that work out of the box.
38 |
39 | - `list_tools()` - Describe available tools
40 | - `call_tool()` - Execute a tool
41 | - `list_resources()` - List available resources
42 | - `read_resource()` - Read resource contents
43 | - `list_prompts()` - List available prompts
44 | - `get_prompt()` - Get prompt details
45 | - `complete()` - Provide auto-completion suggestions
46 |
47 | 3. **Build locally** (requires WASM target):
48 | ```sh
49 | cargo build --release --target wasm32-wasip1
50 | ```
51 | The compiled WASM module will be at: `target/wasm32-wasip1/release/plugin.wasm`
52 |
53 | ### Dependencies
54 |
55 | The template includes key dependencies:
56 |
57 | - **extism-pdk** - Plugin Development Kit for Extism
58 | - **serde/serde_json** - JSON serialization/deserialization
59 | - **anyhow** - Error handling
60 | - **base64** - Base64 encoding/decoding
61 | - **chrono** - Date/time handling
62 |
63 | ## Plugin Handler Functions
64 |
65 | Your plugin can implement any combination of the following handlers. **Only implement the handlers your plugin needs** - the template provides sensible defaults for everything else:
66 |
67 | | Handler | Purpose | Required For |
68 | |---------|---------|--------------|
69 | | `list_tools()` | Declare available tools | Tool-providing plugins |
70 | | `call_tool()` | Execute a tool | Tool-providing plugins |
71 | | `list_resources()` | Declare available resources | Resource-providing plugins |
72 | | `list_resource_templates()` | Declare resource templates | Dynamic resource plugins |
73 | | `read_resource()` | Read resource contents | Resource-providing plugins |
74 | | `list_prompts()` | Declare available prompts | Prompt-providing plugins |
75 | | `get_prompt()` | Retrieve a specific prompt | Prompt-providing plugins |
76 | | `complete()` | Provide auto-completions | Plugins supporting completions |
77 | | `on_roots_list_changed()` | Handle root changes | Plugins reacting to root changes |
78 |
79 | **Example: Tools-only plugin**
80 |
81 | If your plugin only provides tools, you only need to implement:
82 |
83 | ```rust
84 | pub(crate) fn list_tools(_input: ListToolsRequest) -> Result<ListToolsResult> {
85 | // Return your tools
86 | }
87 |
88 | pub(crate) fn call_tool(input: CallToolRequest) -> Result<CallToolResult> {
89 | // Execute the requested tool
90 | }
91 | ```
92 |
93 | All other handlers will use their default implementations.
94 |
95 | ## Host Functions
96 |
97 | Your plugin can call these host functions to interact with the client and MCP server. Import them from the `pdk` module:
98 |
99 | ```rust
100 | use crate::pdk::imports::*;
101 | ```
102 |
103 | ### User Interaction
104 |
105 | **`create_elicitation(input: ElicitRequestParamWithTimeout) -> Result<ElicitResult>`**
106 |
107 | Request user input through the client's elicitation interface. Use this when your plugin needs user guidance, decisions, or confirmations during execution.
108 |
109 | ```rust
110 | let result = create_elicitation(ElicitRequestParamWithTimeout {
111 | request: ElicitRequestParam {
112 | // Define what input you're requesting
113 | ..Default::default()
114 | },
115 | timeout_ms: Some(30000), // 30 second timeout
116 | })?;
117 | ```
118 |
119 | ### Message Generation
120 |
121 | **`create_message(input: CreateMessageRequestParam) -> Result<CreateMessageResult>`**
122 |
123 | Request message creation through the client's sampling interface. Use this when your plugin needs intelligent text generation or analysis with AI assistance.
124 |
125 | ```rust
126 | let result = create_message(CreateMessageRequestParam {
127 | messages: vec![/* conversation history */],
128 | model_preferences: Some(/* model preferences */),
129 | system: Some("You are a helpful assistant".to_string()),
130 | ..Default::default()
131 | })?;
132 | ```
133 |
134 | ### Resource Discovery
135 |
136 | **`list_roots() -> Result<ListRootsResult>`**
137 |
138 | List the client's root directories or resources. Use this to discover what root resources (typically file system roots) are available and understand the scope of resources your plugin can access.
139 |
140 | ```rust
141 | let roots = list_roots()?;
142 | for root in roots.roots {
143 | println!("Root: {} at {}", root.name, root.uri);
144 | }
145 | ```
146 |
147 | ### Logging
148 |
149 | **`notify_logging_message(input: LoggingMessageNotificationParam) -> Result<()>`**
150 |
151 | Send diagnostic, informational, warning, or error messages to the client. The client's logging level determines which messages are processed and displayed.
152 |
153 | ```rust
154 | notify_logging_message(LoggingMessageNotificationParam {
155 | level: "info".to_string(),
156 | logger: Some("my_plugin".to_string()),
157 | data: serde_json::json!({"message": "Processing started"}),
158 | })?;
159 | ```
160 |
161 | ### Progress Reporting
162 |
163 | **`notify_progress(input: ProgressNotificationParam) -> Result<()>`**
164 |
165 | Report progress during long-running operations. Allows clients to display progress bars or status information to users.
166 |
167 | ```rust
168 | notify_progress(ProgressNotificationParam {
169 | progress: 50,
170 | total: Some(100),
171 | })?;
172 | ```
173 |
174 | ### List Change Notifications
175 |
176 | Notify the client when your plugin's available items change:
177 |
178 | **`notify_tool_list_changed() -> Result<()>`**
179 | - Call this when you add, remove, or modify available tools
180 |
181 | **`notify_resource_list_changed() -> Result<()>`**
182 | - Call this when you add, remove, or modify available resources
183 |
184 | **`notify_prompt_list_changed() -> Result<()>`**
185 | - Call this when you add, remove, or modify available prompts
186 |
187 | **`notify_resource_updated(input: ResourceUpdatedNotificationParam) -> Result<()>`**
188 | - Call this when you modify the contents of a specific resource
189 |
190 | ```rust
191 | // When your plugin's tools change
192 | notify_tool_list_changed()?;
193 |
194 | // When a specific resource is updated
195 | notify_resource_updated(ResourceUpdatedNotificationParam {
196 | uri: "resource://my-resource".to_string(),
197 | })?;
198 | ```
199 |
200 | ### Example: Interactive Tool with Progress
201 |
202 | ```rust
203 | pub(crate) fn call_tool(input: CallToolRequest) -> Result<CallToolResult> {
204 | match input.name.as_str() {
205 | "long_task" => {
206 | // Log start
207 | notify_logging_message(LoggingMessageNotificationParam {
208 | level: "info".to_string(),
209 | data: serde_json::json!({"message": "Starting long task"}),
210 | ..Default::default()
211 | })?;
212 |
213 | // Do work with progress updates
214 | for i in 0..10 {
215 | // ... do work ...
216 | notify_progress(ProgressNotificationParam {
217 | progress: (i + 1) * 10,
218 | total: Some(100),
219 | })?;
220 | }
221 |
222 | Ok(CallToolResult {
223 | content: vec![Content {
224 | type_: "text".to_string(),
225 | text: Some("Task completed".to_string()),
226 | ..Default::default()
227 | }],
228 | ..Default::default()
229 | })
230 | },
231 | _ => Err(anyhow!("Unknown tool")),
232 | }
233 | }
234 | ```
235 |
236 | ## Building for Distribution
237 |
238 | ### Using Docker
239 |
240 | The included `Dockerfile` provides a multi-stage build that compiles your plugin to WebAssembly:
241 |
242 | ```sh
243 | docker build -t your-registry/your-plugin-name .
244 | docker push your-registry/your-plugin-name
245 | ```
246 |
247 | The Docker build:
248 | 1. Compiles your Rust code to `wasm32-wasip1` target
249 | 2. Creates a minimal image containing only the compiled `plugin.wasm`
250 | 3. Outputs an OCI-compatible container image
251 |
252 | ### Manual Build
253 |
254 | To build manually without Docker:
255 |
256 | ```sh
257 | # Install dependencies
258 | rustup target add wasm32-wasip1
259 | cargo install cargo-auditable
260 |
261 | # Build
262 | cargo auditable build --release --target wasm32-wasip1
263 |
264 | # Result is at: target/wasm32-wasip1/release/plugin.wasm
265 | ```
266 |
267 | ## Implementation Guide
268 |
269 | ### Creating a Tool
270 |
271 | Here's an example of implementing a simple tool:
272 |
273 | ```rust
274 | pub(crate) fn list_tools(_input: ListToolsRequest) -> Result<ListToolsResult> {
275 | Ok(ListToolsResult {
276 | tools: vec![
277 | Tool {
278 | name: "greet".to_string(),
279 | description: Some("Greet a person".to_string()),
280 | input_schema: json!({
281 | "type": "object",
282 | "properties": {
283 | "name": {
284 | "type": "string",
285 | "description": "The person's name"
286 | }
287 | },
288 | "required": ["name"]
289 | }),
290 | },
291 | ],
292 | ..Default::default()
293 | })
294 | }
295 |
296 | pub(crate) fn call_tool(input: CallToolRequest) -> Result<CallToolResult> {
297 | match input.name.as_str() {
298 | "greet" => {
299 | let name = input.arguments
300 | .get("name")
301 | .and_then(|v| v.as_str())
302 | .ok_or_else(|| anyhow!("name argument required"))?;
303 |
304 | Ok(CallToolResult {
305 | content: vec![Content {
306 | type_: "text".to_string(),
307 | text: Some(format!("Hello, {}!", name)),
308 | ..Default::default()
309 | }],
310 | ..Default::default()
311 | })
312 | },
313 | _ => Err(anyhow!("Unknown tool: {}", input.name)),
314 | }
315 | }
316 | ```
317 |
318 | ### Creating a Resource
319 |
320 | Example of implementing a resource:
321 |
322 | ```rust
323 | pub(crate) fn list_resources(_input: ListResourcesRequest) -> Result<ListResourcesResult> {
324 | Ok(ListResourcesResult {
325 | resources: vec![
326 | ResourceDescription {
327 | uri: "resource://example".to_string(),
328 | name: Some("Example Resource".to_string()),
329 | description: Some("An example resource".to_string()),
330 | mime_type: Some("text/plain".to_string()),
331 | },
332 | ],
333 | ..Default::default()
334 | })
335 | }
336 |
337 | pub(crate) fn read_resource(input: ReadResourceRequest) -> Result<ReadResourceResult> {
338 | match input.uri.as_str() {
339 | "resource://example" => Ok(ReadResourceResult {
340 | contents: vec![ResourceContents {
341 | mime_type: Some("text/plain".to_string()),
342 | text: Some("Resource content here".to_string()),
343 | ..Default::default()
344 | }],
345 | }),
346 | _ => Err(anyhow!("Unknown resource: {}", input.uri)),
347 | }
348 | }
349 | ```
350 |
351 | ## Configuration in hyper-mcp
352 |
353 | After building and publishing your plugin, configure it in hyper-mcp:
354 |
355 | ```json
356 | {
357 | "plugins": {
358 | "my_plugin": {
359 | "url": "oci://your-registry/your-plugin-name:latest"
360 | }
361 | }
362 | }
363 | ```
364 |
365 | For local development/testing:
366 |
367 | ```json
368 | {
369 | "plugins": {
370 | "my_plugin": {
371 | "url": "file:///path/to/target/wasm32-wasip1/release/plugin.wasm"
372 | }
373 | }
374 | }
375 | ```
376 |
377 | ## Testing
378 |
379 | To test your plugin locally:
380 |
381 | 1. Build it: `cargo build --release --target wasm32-wasip1`
382 | 2. Update hyper-mcp's config to point to `file://` URL
383 | 3. Start hyper-mcp with `RUST_LOG=debug`
384 | 4. Test through Claude Desktop, Cursor IDE, or another MCP client
385 |
386 | ## Resources
387 |
388 | - [hyper-mcp Documentation](https://github.com/tuananh/hyper-mcp)
389 | - [MCP Protocol Specification](https://spec.modelcontextprotocol.io/)
390 | - [Extism Plugin Development Kit](https://docs.extism.org/docs/pdk)
391 | - [Example Plugins](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins)
392 |
393 | ## License
394 |
395 | Same as hyper-mcp - Apache 2.0
396 |
```
--------------------------------------------------------------------------------
/templates/plugins/go/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Go Plugin Template
2 |
3 | A WebAssembly plugin template for building MCP (Model Context Protocol) plugins in Go using the hyper-mcp framework.
4 |
5 | ## Overview
6 |
7 | This template provides a starter project for creating MCP plugins that run as WebAssembly modules. It includes all necessary dependencies and boilerplate code to implement MCP protocol handlers.
8 |
9 | ## Project Structure
10 |
11 | ```
12 | .
13 | ├── main.go # Plugin handler implementations
14 | ├── exports.go # WASM export wrappers for handlers
15 | ├── imports.go # Host function calls
16 | ├── types.go # MCP protocol types
17 | ├── go.mod # Go module definition
18 | ├── go.sum # Go module checksums
19 | ├── Dockerfile # Multi-stage build for compiling to WASM
20 | └── .gitignore # Git ignore rules
21 | ```
22 |
23 | ## Getting Started
24 |
25 | ### Prerequisites
26 |
27 | - Go 1.22 or later
28 | - Docker (for building WASM)
29 | - `clang` and `lld` (for WASM compilation)
30 |
31 | ### Development
32 |
33 | 1. **Clone or use this template** to start your plugin project
34 |
35 | 2. **Implement plugin handlers** in `main.go`:
36 |
37 | Plugin handlers must be implemented without the use of goroutines *unless* you modify the Dockerfile build to remove `-scheduler=none` from the tinygo build flags. Note that this is not recommended, as hyper-mcp will normally handle concurrent executions for you.
38 |
39 | > **Note:** You only need to implement the handlers relevant to your plugin. For example, if your plugin only provides tools, implement only `ListTools()` and `CallTool()`. All other handlers have default implementations that work out of the box.
40 |
41 | - `ListTools()` - Describe available tools
42 | - `CallTool()` - Execute a tool
43 | - `ListResources()` - List available resources
44 | - `ReadResource()` - Read resource contents
45 | - `ListPrompts()` - List available prompts
46 | - `GetPrompt()` - Get prompt details
47 | - `Complete()` - Provide auto-completion suggestions
48 | - `ListResourceTemplates()` - List resource templates
49 | - `OnRootsListChanged()` - Handle root changes
50 |
51 | 3. **Build locally** (requires Docker for WASM target):
52 | ```sh
53 | docker build -t your-plugin-name .
54 | docker run --rm -v $(pwd):/workspace your-plugin-name cp /plugin.wasm /workspace/
55 | ```
56 |
57 | ### Dependencies
58 |
59 | The template uses:
60 |
61 | - **extism/go-pdk** - Plugin Development Kit for Extism
62 | - Standard Go libraries for JSON serialization and time handling
63 |
64 | ## Plugin Handler Functions
65 |
66 | Your plugin can implement any combination of the following handlers. **Only implement the handlers your plugin needs** - the template provides sensible defaults for everything else:
67 |
68 | | Handler | Purpose | Required For |
69 | |---------|---------|--------------|
70 | | `ListTools()` | Declare available tools | Tool-providing plugins |
71 | | `CallTool()` | Execute a tool | Tool-providing plugins |
72 | | `ListResources()` | Declare available resources | Resource-providing plugins |
73 | | `ListResourceTemplates()` | Declare resource templates | Dynamic resource plugins |
74 | | `ReadResource()` | Read resource contents | Resource-providing plugins |
75 | | `ListPrompts()` | Declare available prompts | Prompt-providing plugins |
76 | | `GetPrompt()` | Retrieve a specific prompt | Prompt-providing plugins |
77 | | `Complete()` | Provide auto-completions | Plugins supporting completions |
78 | | `OnRootsListChanged()` | Handle root changes | Plugins reacting to root changes |
79 |
80 | **Example: Tools-only plugin**
81 |
82 | If your plugin only provides tools, you only need to implement:
83 |
84 | ```go
85 | func ListTools(input ListToolsRequest) (*ListToolsResult, error) {
86 | return &ListToolsResult{
87 | Tools: []Tool{
88 | {
89 | Name: "greet",
90 | Description: ptrString("Greet a person"),
91 | InputSchema: ToolSchema{
92 | Type: "object",
93 | Properties: map[string]interface{}{
94 | "name": map[string]interface{}{
95 | "type": "string",
96 | "description": "The person's name",
97 | },
98 | },
99 | Required: []string{"name"},
100 | },
101 | },
102 | },
103 | }, nil
104 | }
105 |
106 | func CallTool(input CallToolRequest) (*CallToolResult, error) {
107 | switch input.Request.Name {
108 | case "greet":
109 | name, ok := input.Request.Arguments["name"].(string)
110 | if !ok {
111 | return &CallToolResult{
112 | Content: []json.RawMessage{
113 | []byte(`{"type":"text","text":"name argument required"}`),
114 | },
115 | }, nil
116 | }
117 | return &CallToolResult{
118 | Content: []json.RawMessage{
119 | []byte(fmt.Sprintf(`{"type":"text","text":"Hello, %s!"}`, name)),
120 | },
121 | }, nil
122 | default:
123 | return &CallToolResult{
124 | Content: []json.RawMessage{
125 | []byte(fmt.Sprintf(`{"type":"text","text":"Unknown tool: %s"}`, input.Request.Name)),
126 | },
127 | }, nil
128 | }
129 | }
130 | ```
131 |
132 | All other handlers will use their default implementations.
133 |
134 | ## Host Functions
135 |
136 | Your plugin can call these host functions to interact with the client and MCP server. Available through direct function calls in `imports.go`:
137 |
138 | ```go
139 | // Example usage
140 | result, err := CreateElicitation(ElicitRequestParamWithTimeout{...})
141 | ```
142 |
143 | ### User Interaction
144 |
145 | **`CreateElicitation(input ElicitRequestParamWithTimeout) (*ElicitResult, error)`**
146 |
147 | Request user input through the client's elicitation interface. Use this when your plugin needs user guidance, decisions, or confirmations during execution.
148 |
149 | ```go
150 | result, err := CreateElicitation(ElicitRequestParamWithTimeout{
151 | Message: "Please provide your name",
152 | RequestedSchema: Schema{
153 | Type: "object",
154 | Properties: map[string]json.RawMessage{
155 | "name": json.RawMessage(`{"type":"string"}`),
156 | },
157 | },
158 | Timeout: ptrInt64(30000), // 30 second timeout
159 | })
160 | ```
161 |
162 | ### Message Generation
163 |
164 | **`CreateMessage(input CreateMessageRequestParam) (*CreateMessageResult, error)`**
165 |
166 | Request message creation through the client's sampling interface. Use this when your plugin needs intelligent text generation or analysis with AI assistance.
167 |
168 | ```go
169 | result, err := CreateMessage(CreateMessageRequestParam{
170 | MaxTokens: 1024,
171 | Messages: []json.RawMessage{
172 | // conversation history
173 | },
174 | SystemPrompt: ptrString("You are a helpful assistant"),
175 | })
176 | ```
177 |
178 | ### Resource Discovery
179 |
180 | **`ListRoots() (*ListRootsResult, error)`**
181 |
182 | List the client's root directories or resources. Use this to discover what root resources (typically file system roots) are available and understand the scope of resources your plugin can access.
183 |
184 | ```go
185 | roots, err := ListRoots()
186 | if err == nil {
187 | for _, root := range roots.Roots {
188 | fmt.Printf("Root: %s at %s\n", *root.Name, root.URI)
189 | }
190 | }
191 | ```
192 |
193 | ### Logging
194 |
195 | **`NotifyLoggingMessage(input LoggingMessageNotificationParam) error`**
196 |
197 | Send diagnostic, informational, warning, or error messages to the client. The client's logging level determines which messages are processed and displayed.
198 |
199 | ```go
200 | NotifyLoggingMessage(LoggingMessageNotificationParam{
201 | Level: LoggingLevelInfo,
202 | Logger: ptrString("my_plugin"),
203 | Data: json.RawMessage(`{"message": "Processing started"}`),
204 | })
205 | ```
206 |
207 | ### Progress Reporting
208 |
209 | **`NotifyProgress(input ProgressNotificationParam) error`**
210 |
211 | Report progress during long-running operations. Allows clients to display progress bars or status information to users.
212 |
213 | ```go
214 | NotifyProgress(ProgressNotificationParam{
215 | Progress: 50,
216 | ProgressToken: "task-1",
217 | Total: ptrFloat64(100),
218 | })
219 | ```
220 |
221 | ### List Change Notifications
222 |
223 | Notify the client when your plugin's available items change:
224 |
225 | **`NotifyToolListChanged() error`**
226 | - Call this when you add, remove, or modify available tools
227 |
228 | **`NotifyResourceListChanged() error`**
229 | - Call this when you add, remove, or modify available resources
230 |
231 | **`NotifyPromptListChanged() error`**
232 | - Call this when you add, remove, or modify available prompts
233 |
234 | **`NotifyResourceUpdated(input ResourceUpdatedNotificationParam) error`**
235 | - Call this when you modify the contents of a specific resource
236 |
237 | ```go
238 | // When your plugin's tools change
239 | NotifyToolListChanged()
240 |
241 | // When a specific resource is updated
242 | NotifyResourceUpdated(ResourceUpdatedNotificationParam{
243 | URI: "resource://my-resource",
244 | })
245 | ```
246 |
247 | ### Example: Interactive Tool with Progress
248 |
249 | ```go
250 | func CallTool(input CallToolRequest) (*CallToolResult, error) {
251 | switch input.Request.Name {
252 | case "long_task":
253 | // Log start
254 | NotifyLoggingMessage(LoggingMessageNotificationParam{
255 | Level: LoggingLevelInfo,
256 | Data: json.RawMessage(`{"message": "Starting long task"}`),
257 | })
258 |
259 | // Do work with progress updates
260 | for i := 0; i < 10; i++ {
261 | // ... do work ...
262 | NotifyProgress(ProgressNotificationParam{
263 | Progress: float64((i + 1) * 10),
264 | ProgressToken: "task-1",
265 | Total: ptrFloat64(100),
266 | })
267 | }
268 |
269 | return &CallToolResult{
270 | Content: []json.RawMessage{
271 | []byte(`{"type":"text","text":"Task completed"}`),
272 | },
273 | }, nil
274 | default:
275 | return &CallToolResult{
276 | Content: []json.RawMessage{
277 | []byte(fmt.Sprintf(`{"type":"text","text":"Unknown tool: %s"}`, input.Request.Name)),
278 | },
279 | }, nil
280 | }
281 | }
282 | ```
283 |
284 | ## Building for Distribution
285 |
286 | ### Using Docker
287 |
288 | The included `Dockerfile` provides a multi-stage build that compiles your plugin to WebAssembly:
289 |
290 | ```sh
291 | docker build -t your-registry/your-plugin-name .
292 | docker run --rm -v $(pwd):/workspace your-registry/your-plugin-name cp /plugin.wasm /workspace/
293 | ```
294 |
295 | The Docker build:
296 | 1. Compiles your Go code to `wasip1` target
297 | 2. Creates a minimal image containing only the compiled `plugin.wasm`
298 | 3. Outputs an OCI-compatible container image
299 |
300 | ### Manual Build
301 |
302 | To build manually without Docker (requires Go 1.22+):
303 |
304 | ```sh
305 | # Build for WASM
306 | GOOS=wasip1 GOARCH=wasm CGO_ENABLED=0 go build -o plugin.wasm ./
307 |
308 | # Result is at: plugin.wasm
309 | ```
310 |
311 | ## Implementation Guide
312 |
313 | ### Creating a Tool
314 |
315 | Here's an example of implementing a simple tool:
316 |
317 | ```go
318 | func ListTools(input ListToolsRequest) (*ListToolsResult, error) {
319 | return &ListToolsResult{
320 | Tools: []Tool{
321 | {
322 | Name: "greet",
323 | Description: ptrString("Greet a person"),
324 | InputSchema: ToolSchema{
325 | Type: "object",
326 | Properties: map[string]interface{}{
327 | "name": map[string]interface{}{
328 | "type": "string",
329 | "description": "The person's name",
330 | },
331 | },
332 | Required: []string{"name"},
333 | },
334 | },
335 | },
336 | }, nil
337 | }
338 |
339 | func CallTool(input CallToolRequest) (*CallToolResult, error) {
340 | switch input.Request.Name {
341 | case "greet":
342 | name, ok := input.Request.Arguments["name"].(string)
343 | if !ok {
344 | return &CallToolResult{
345 | Content: []json.RawMessage{
346 | []byte(`{"type":"text","text":"name argument required"}`),
347 | },
348 | }, nil
349 | }
350 | return &CallToolResult{
351 | Content: []json.RawMessage{
352 | []byte(fmt.Sprintf(`{"type":"text","text":"Hello, %s!"}`, name)),
353 | },
354 | }, nil
355 | default:
356 | return &CallToolResult{
357 | Content: []json.RawMessage{
358 | []byte(fmt.Sprintf(`{"type":"text","text":"Unknown tool: %s"}`, input.Request.Name)),
359 | },
360 | }, nil
361 | }
362 | }
363 | ```
364 |
365 | ### Creating a Resource
366 |
367 | Example of implementing a resource:
368 |
369 | ```go
370 | func ListResources(input ListResourcesRequest) (*ListResourcesResult, error) {
371 | return &ListResourcesResult{
372 | Resources: []Resource{
373 | {
374 | URI: "resource://example",
375 | Name: "Example Resource",
376 | Description: ptrString("An example resource"),
377 | MimeType: ptrString("text/plain"),
378 | },
379 | },
380 | }, nil
381 | }
382 |
383 | func ReadResource(input ReadResourceRequest) (*ReadResourceResult, error) {
384 | switch input.Request.URI {
385 | case "resource://example":
386 | return &ReadResourceResult{
387 | Contents: []json.RawMessage{
388 | []byte(`{"uri":"resource://example","mimeType":"text/plain","text":"Resource content here"}`),
389 | },
390 | }, nil
391 | default:
392 | return &ReadResourceResult{
393 | Contents: []json.RawMessage{
394 | []byte(fmt.Sprintf(`{"type":"text","text":"Unknown resource: %s"}`, input.Request.URI)),
395 | },
396 | }, nil
397 | }
398 | }
399 | ```
400 |
401 | ## Helper Functions
402 |
403 | The template includes some useful helper functions for working with pointers:
404 |
405 | ```go
406 | // Helper to create string pointers
407 | func ptrString(s string) *string {
408 | return &s
409 | }
410 |
411 | // Helper to create int64 pointers
412 | func ptrInt64(i int64) *int64 {
413 | return &i
414 | }
415 |
416 | // Helper to create float64 pointers
417 | func ptrFloat64(f float64) *float64 {
418 | return &f
419 | }
420 |
421 | // Helper to create bool pointers
422 | func ptrBool(b bool) *bool {
423 | return &b
424 | }
425 | ```
426 |
427 | ## Configuration in hyper-mcp
428 |
429 | After building and publishing your plugin, configure it in hyper-mcp:
430 |
431 | ```json
432 | {
433 | "plugins": {
434 | "my_plugin": {
435 | "url": "oci://your-registry/your-plugin-name:latest"
436 | }
437 | }
438 | }
439 | ```
440 |
441 | For local development/testing:
442 |
443 | ```json
444 | {
445 | "plugins": {
446 | "my_plugin": {
447 | "url": "file:///path/to/plugin.wasm"
448 | }
449 | }
450 | }
451 | ```
452 |
453 | ## Testing
454 |
455 | To test your plugin locally:
456 |
457 | 1. Build it: `docker build -t my-plugin . && docker run --rm -v $(pwd):/workspace my-plugin cp /plugin.wasm /workspace/`
458 | 2. Update hyper-mcp's config to point to `file://` URL
459 | 3. Start hyper-mcp with `RUST_LOG=debug`
460 | 4. Test through Claude Desktop, Cursor IDE, or another MCP client
461 |
462 | ## Resources
463 |
464 | - [hyper-mcp Documentation](https://github.com/tuananh/hyper-mcp)
465 | - [MCP Protocol Specification](https://spec.modelcontextprotocol.io/)
466 | - [Extism Go PDK](https://github.com/extism/go-pdk)
467 | - [WebAssembly Documentation](https://webassembly.org/)
468 | - [Example Plugins](https://github.com/tuananh/hyper-mcp/tree/main/examples/plugins)
469 |
470 | ## License
471 |
472 | Same as hyper-mcp - Apache 2.0
473 |
```
--------------------------------------------------------------------------------
/tests/fixtures/unsupported_config.txt:
--------------------------------------------------------------------------------
```
1 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/arxiv/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/context7/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/eval-py/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fetch/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fs/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gitlab/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gomodule/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/maven/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/memory/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/serper/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/sqlite/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/think/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/time/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/examples/plugins/v2/rstime/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/templates/plugins/rust/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 |
```
--------------------------------------------------------------------------------
/src/wasm/mod.rs:
--------------------------------------------------------------------------------
```rust
1 | pub mod http;
2 | pub mod oci;
3 | pub mod s3;
4 |
```
--------------------------------------------------------------------------------
/examples/plugins/v2/rstime/src/pdk/mod.rs:
--------------------------------------------------------------------------------
```rust
1 | pub mod exports;
2 | pub mod imports;
3 | pub mod types;
4 |
```
--------------------------------------------------------------------------------
/templates/plugins/rust/src/pdk/mod.rs:
--------------------------------------------------------------------------------
```rust
1 | pub mod exports;
2 | pub mod imports;
3 | pub mod types;
4 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/.cargo/config.toml:
--------------------------------------------------------------------------------
```toml
1 | [build]
2 | target = "wasm32-wasip1"
3 | rustflags = ["--cfg", "tokio_unstable"]
4 |
```
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
```toml
1 | [toolchain]
2 | channel = "1.90"
3 | components = ["rustc", "rust-std", "cargo", "clippy", "rustfmt"]
4 |
```
--------------------------------------------------------------------------------
/.windsurf/rules/print-ctx-size.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | trigger: always_on
3 | description:
4 | globs:
5 | ---
6 |
7 | # Your rule content
8 |
9 | - End every request with "Total context size: ~nk tokens" and list the files you have in view.
10 |
```
--------------------------------------------------------------------------------
/tests/fixtures/invalid_plugin_name.yaml:
--------------------------------------------------------------------------------
```yaml
1 | plugins:
2 | plugin@name:
3 | url: "file:///path/to/plugin"
4 | runtime_config:
5 | skip_tools:
6 | - "tool1"
7 | - "tool2"
8 | allowed_hosts:
9 | - "example.com"
10 |
11 | valid_plugin:
12 | url: "https://example.com/plugin"
13 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fs/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "fs"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64-serde = "0.7"
15 | base64 = "0.21"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/myip/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "myip"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64 = "0.21"
15 | base64-serde = "0.8.0"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gomodule/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "gomodule"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64-serde = "0.7"
15 | base64 = "0.21"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/think/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "think"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0.219", features = ["derive"] }
13 | serde_json = "1.0.140"
14 | base64-serde = "0.8.0"
15 | base64 = "0.22.1"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/serper/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "serper"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0.219", features = ["derive"] }
13 | serde_json = "1.0.140"
14 | base64-serde = "0.8.0"
15 | base64 = "0.22.1"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "crates-io"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0.219", features = ["derive"] }
13 | serde_json = "1.0.140"
14 | base64-serde = "0.8.0"
15 | base64 = "0.22.1"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/tool-list-changed/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "tool-list-changed"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | name = "tool_list_changed"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64-serde = "0.7"
15 | base64 = "0.21"
16 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qr-code/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "mcp-qr-code"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "qrcode"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64-serde = "0.7"
15 | base64 = "0.21"
16 | qrcode-png = "0.4.1"
17 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fetch/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "fetch"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0.219", features = ["derive"] }
13 | serde_json = "1.0.140"
14 | base64-serde = "0.8.0"
15 | base64 = "0.22.1"
16 | htmd = "0.1.6"
17 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/maven/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "maven"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | quick-xml = "0.31"
12 | extism-pdk = "=1.4.0"
13 | serde = { version = "1.0.219", features = ["derive"] }
14 | serde_json = "1.0.140"
15 | base64-serde = "0.8.0"
16 | base64 = "0.22.1"
17 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crypto-price/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM tinygo/tinygo:0.37.0 AS builder
2 |
3 | WORKDIR /workspace
4 | COPY go.mod .
5 | COPY go.sum .
6 | RUN go mod download
7 | COPY . .
8 | RUN GOOS=wasip1 GOARCH=wasm tinygo build -no-debug -panic=trap -scheduler=none -o plugin.wasm
9 |
10 | FROM scratch
11 | WORKDIR /
12 | COPY --from=builder /workspace/plugin.wasm /plugin.wasm
13 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/github/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM tinygo/tinygo:0.37.0 AS builder
2 |
3 | WORKDIR /workspace
4 | COPY go.mod .
5 | COPY go.sum .
6 | RUN go mod download
7 | COPY . .
8 | RUN GOOS=wasip1 GOARCH=wasm tinygo build -no-debug -panic=trap -scheduler=none -o plugin.wasm
9 |
10 | FROM scratch
11 | WORKDIR /
12 | COPY --from=builder /workspace/plugin.wasm /plugin.wasm
13 |
```
--------------------------------------------------------------------------------
/templates/plugins/go/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM tinygo/tinygo:0.39.0 AS builder
2 |
3 | WORKDIR /workspace
4 | COPY go.mod .
5 | COPY go.sum .
6 | RUN go mod download
7 | COPY . .
8 | RUN GOOS=wasip1 GOARCH=wasm tinygo build -no-debug -panic=trap -scheduler=none -o plugin.wasm
9 |
10 | FROM scratch
11 | WORKDIR /
12 | COPY --from=builder /workspace/plugin.wasm /plugin.wasm
13 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/time/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "time"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "time"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | chrono = { version = "0.4", features = ["serde"] }
13 | serde = { version = "1.0", features = ["derive"] }
14 | serde_json = "1.0"
15 | base64-serde = "0.7"
16 | base64 = "0.21"
17 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/context7/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "context7"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0.219", features = ["derive"] }
13 | serde_json = "1.0.140"
14 | base64-serde = "0.8.0"
15 | base64 = "0.22.1"
16 | htmd = "0.1.6"
17 | urlencoding = "2.1.3"
18 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gitlab/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "gitlab"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64-serde = "0.7"
15 | base64 = "0.21"
16 | urlencoding = "2.1"
17 | url = "2.5"
18 | termtree = "0.5.1"
19 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/hash/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "hash"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64 = "0.21"
15 | base64-serde = "0.8.0"
16 | sha2 = "0.10"
17 | md5 = "0.7"
18 | sha1 = "0.10"
19 | base32 = "0.4"
20 |
```
--------------------------------------------------------------------------------
/tests/fixtures/invalid_url.yaml:
--------------------------------------------------------------------------------
```yaml
1 | plugins:
2 | valid_plugin:
3 | url: "file:///path/to/plugin"
4 | runtime_config:
5 | skip_tools:
6 | - "tool1"
7 | - "tool2"
8 |
9 | invalid_url_plugin:
10 | url: "not a valid url"
11 | runtime_config:
12 | allowed_hosts:
13 | - "example.com"
14 |
15 | another_valid_plugin:
16 | url: "https://example.com/plugin"
17 |
```
--------------------------------------------------------------------------------
/templates/plugins/rust/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "plugin"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | anyhow = "1.0"
12 | base64 = "0.22"
13 | base64-serde = "0.8"
14 | chrono = { version = "0.4", features = ["serde"] }
15 | extism-pdk = "1.4.1"
16 | serde = { version = "1.0", features = ["derive"] }
17 | serde_json = "1.0"
18 |
19 | [workspace]
20 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "qdrant"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0.219", features = ["derive"] }
13 | serde_json = "1.0.140"
14 | base64-serde = "0.8.0"
15 | base64 = "0.22.1"
16 |
17 | anyhow = "1.0.98"
18 | uuid = { version = "1.16.0", features = ["v4"] }
19 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/arxiv/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "arxiv"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | chrono = { version = "0.4", features = ["serde"] }
15 | feed-rs = "1.3"
16 | urlencoding = "2.1"
17 | base64 = "0.21"
18 | base64-serde = "0.8.0"
19 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/sqlite/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "sqlite"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64 = "0.21"
15 | base64-serde = "0.8.0"
16 | rusqlite = { version = "0.34.0", features = ["bundled"] }
17 |
18 | [build-dependencies]
19 | cc = "1.0"
20 |
```
--------------------------------------------------------------------------------
/examples/plugins/v2/rstime/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "rstime"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | anyhow = "1.0"
12 | base64 = "0.21"
13 | base64-serde = "0.7"
14 | chrono = { version = "0.4", features = ["serde"] }
15 | chrono-tz = "0.10"
16 | extism-pdk = "1.4.1"
17 | serde = { version = "1.0", features = ["derive"] }
18 | serde_json = "1.0"
19 |
20 | [workspace]
21 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/time/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/time.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/arxiv/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/context7/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/crates-io/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/eval-py/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fetch/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/fs/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gitlab/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/gomodule/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/hash/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/maven/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/myip/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qdrant/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/qr-code/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/qrcode.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/serper/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/think/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v2/rstime/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.90-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/templates/plugins/rust/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.90-slim AS builder
2 |
3 | RUN rustup target add wasm32-wasip1 && \
4 | rustup component add rust-std --target wasm32-wasip1 && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-wasip1
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-wasip1/release/plugin.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/memory/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "memory"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | base64 = "0.21"
15 | base64-serde = "0.8.0"
16 | uuid = { version = "1.16", features = ["v4", "serde"] }
17 | rusqlite = { version = "0.34.0", features = ["bundled"] }
18 |
19 | [build-dependencies]
20 | cc = "1.0"
21 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/meme-generator/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "meme-generator"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | ab_glyph = "0.2.29"
12 | image = "0.25.6"
13 | imageproc = "0.25.0"
14 | rusttype = "0.9.3"
15 | serde = { version = "1.0", features = ["derive"] }
16 | serde_json = "1.0"
17 | reqwest = { version = "0.11", features = ["blocking"] }
18 | serde_yaml = "0.9"
19 | extism-pdk = "=1.4.0"
20 | base64-serde = "0.7"
21 | base64 = "0.21"
22 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/tool-list-changed/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM rust:1.88-slim AS builder
2 |
3 | RUN rustup target add wasm32-unknown-unknown && \
4 | rustup component add rust-std --target wasm32-unknown-unknown && \
5 | cargo install cargo-auditable
6 |
7 | WORKDIR /workspace
8 | COPY . .
9 | RUN cargo fetch
10 | RUN cargo auditable build --release --target wasm32-unknown-unknown
11 |
12 | FROM scratch
13 | WORKDIR /
14 | COPY --from=builder /workspace/target/wasm32-unknown-unknown/release/tool_list_changed.wasm /plugin.wasm
15 |
```
--------------------------------------------------------------------------------
/tests/fixtures/invalid_structure.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # This is an invalid structure - missing the 'plugins' top-level map
2 | test_plugin:
3 | url: "file:///path/to/plugin"
4 | runtime_config:
5 | skip_tools:
6 | - "tool1"
7 | - "tool2"
8 |
9 | # This puts plugins at the wrong level
10 | configuration:
11 | plugins:
12 | another_plugin:
13 | url: "https://example.com/plugin"
14 |
15 | # This has the correct top-level key but incorrect structure
16 | plugins:
17 | - name: minimal_plugin
18 | url: "http://localhost:3000/plugin"
19 |
```
--------------------------------------------------------------------------------
/examples/plugins/v1/eval-py/Cargo.toml:
--------------------------------------------------------------------------------
```toml
1 | [package]
2 | name = "eval-py"
3 | version = "0.1.0"
4 | edition = "2024"
5 |
6 | [lib]
7 | name = "plugin"
8 | crate-type = ["cdylib"]
9 |
10 | [dependencies]
11 | extism-pdk = "=1.4.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 | serde_json = "1.0"
14 | rustpython-vm = { version = "0.4.0", default-features = false, features = ["compiler"] }
15 | base64-serde = "0.8.0"
16 | base64 = "0.22.1"
17 |
18 | [profile.release]
19 | lto = true
20 | opt-level = 's'
21 | strip = true
22 |
23 | [target.wasm32-wasi.dependencies]
24 | getrandom = { version = "0.2", features = ["js"] }
25 |
```
--------------------------------------------------------------------------------
/src/logging.rs:
--------------------------------------------------------------------------------
```rust
1 | use once_cell::sync::OnceCell;
2 | use tracing_subscriber::EnvFilter;
3 |
4 | static LOGGING: OnceCell<()> = OnceCell::new();
5 |
6 | #[ctor::ctor]
7 | fn _install_global_tracing() {
8 | LOGGING.get_or_init(|| {
9 | tracing_subscriber::fmt()
10 | .with_env_filter(
11 | EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
12 | )
13 | .with_test_writer()
14 | .with_target(true)
15 | .with_line_number(true)
16 | .with_ansi(false)
17 | .init();
18 | });
19 | }
20 |
```
--------------------------------------------------------------------------------
/tests/fixtures/valid_config.yaml:
--------------------------------------------------------------------------------
```yaml
1 | plugins:
2 | test_plugin:
3 | url: "file:///path/to/plugin"
4 | runtime_config:
5 | skip_tools:
6 | - "tool1"
7 | - "tool2"
8 | allowed_hosts:
9 | - "example.com"
10 | - "localhost"
11 | allowed_paths:
12 | - "/tmp"
13 | - "/var/log"
14 | env_vars:
15 | DEBUG: "true"
16 | LOG_LEVEL: "info"
17 | memory_limit: "1GB"
18 |
19 | another_plugin:
20 | url: "https://example.com/plugin"
21 | runtime_config:
22 | allowed_hosts:
23 | - "api.example.com"
24 |
25 | minimal_plugin:
26 | url: "http://localhost:3000/plugin"
27 |
```
--------------------------------------------------------------------------------
/tests/fixtures/invalid_auth_config.yaml:
--------------------------------------------------------------------------------
```yaml
1 | auths:
2 | "https://api.example.com":
3 | type: invalid_type
4 | username: testuser
5 | password: testpass
6 | "https://secure.api.com":
7 | type: basic
8 | # Missing password field
9 | username: testuser
10 | "https://oauth.service.com":
11 | type: token
12 | # Missing token field
13 | "https://malformed.com":
14 | # Missing type field
15 | username: testuser
16 | password: testpass
17 | "https://keyring-incomplete.com":
18 | type: keyring
19 | service: test-service
20 | # Missing user field
21 | plugins:
22 | test_plugin:
23 | url: "file:///path/to/plugin"
24 |
```
--------------------------------------------------------------------------------
/tests/fixtures/valid_config.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "plugins": {
3 | "test_plugin": {
4 | "url": "file:///path/to/plugin",
5 | "runtime_config": {
6 | "skip_tools": ["tool1", "tool2"],
7 | "allowed_hosts": ["example.com", "localhost"],
8 | "allowed_paths": ["/tmp", "/var/log"],
9 | "env_vars": {
10 | "DEBUG": "true",
11 | "LOG_LEVEL": "info"
12 | },
13 | "memory_limit": "1GB"
14 | }
15 | },
16 | "another_plugin": {
17 | "url": "https://example.com/plugin",
18 | "runtime_config": {
19 | "allowed_hosts": ["api.example.com"]
20 | }
21 | },
22 | "minimal_plugin": {
23 | "url": "http://localhost:3000/plugin"
24 | }
25 | }
26 | }
27 |
```
--------------------------------------------------------------------------------
/tests/fixtures/config_with_auths.yaml:
--------------------------------------------------------------------------------
```yaml
1 | auths:
2 | "https://api.example.com":
3 | type: basic
4 | username: testuser
5 | password: testpass
6 | "https://secure.api.com":
7 | type: token
8 | token: bearer-token-123
9 | "https://private.registry.io":
10 | type: basic
11 | username: admin
12 | password: secretpass
13 | "https://oauth.service.com":
14 | type: token
15 | token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
16 | plugins:
17 | test_plugin:
18 | url: "file:///path/to/plugin"
19 | runtime_config:
20 | allowed_hosts:
21 | - "api.example.com"
22 | - "secure.api.com"
23 | auth_test_plugin:
24 | url: "https://secure.api.com/plugin"
25 | runtime_config:
26 | allowed_hosts:
27 | - "secure.api.com"
28 |
```
--------------------------------------------------------------------------------
/server.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
3 | "name": "io.github.tuananh/hyper-mcp",
4 | "description": "📦️ A fast, secure MCP server that extends its capabilities through WebAssembly plugins",
5 | "status": "active",
6 | "repository": {
7 | "url": "https://github.com/tuananh/hyper-mcp",
8 | "source": "github"
9 | },
10 | "version": "1.0.0",
11 | "packages": [
12 | {
13 | "registry_type": "oci",
14 | "registry_base_url": "https://docker.io",
15 | "identifier": "tuananh/hyper-mcp",
16 | "version": "v0.1.6",
17 | "transport": {
18 | "type": "stdio"
19 | },
20 | "environment_variables": []
21 | }
22 | ]
23 | }
24 |
```
--------------------------------------------------------------------------------
/tests/fixtures/documentation_example.yaml:
--------------------------------------------------------------------------------
```yaml
1 | auths:
2 | "https://private.registry.io":
3 | type: basic
4 | username: "registry-user"
5 | password: "registry-pass"
6 | "https://api.github.com":
7 | type: token
8 | token: "ghp_1234567890abcdef"
9 | "https://enterprise.api.com":
10 | type: basic
11 | username: "enterprise-user"
12 | password: "enterprise-pass"
13 |
14 | plugins:
15 | time:
16 | url: oci://ghcr.io/tuananh/time-plugin:latest
17 | myip:
18 | url: oci://ghcr.io/tuananh/myip-plugin:latest
19 | runtime_config:
20 | allowed_hosts:
21 | - "1.1.1.1"
22 | skip_tools:
23 | - "debug"
24 | env_vars:
25 | FOO: "bar"
26 | memory_limit: "512Mi"
27 | private_plugin:
28 | url: "https://private.registry.io/my_plugin"
29 | runtime_config:
30 | allowed_hosts:
31 | - "private.registry.io"
32 |
```
--------------------------------------------------------------------------------
/tests/fixtures/keyring_auth_config.yaml:
--------------------------------------------------------------------------------
```yaml
1 | auths:
2 | "https://private.registry.io":
3 | type: keyring
4 | service: "private-registry"
5 | user: "registry-user"
6 | "https://internal.api.com":
7 | type: keyring
8 | service: "internal-api"
9 | user: "api-user"
10 | "https://secure.vault.com":
11 | type: keyring
12 | service: "vault-service"
13 | user: "vault-admin"
14 | "https://enterprise.github.com":
15 | type: keyring
16 | service: "github-enterprise"
17 | user: "enterprise-user"
18 | plugins:
19 | secure-plugin:
20 | url: "https://private.registry.io/plugin"
21 | runtime_config:
22 | allowed_hosts:
23 | - "private.registry.io"
24 | internal-plugin:
25 | url: "https://internal.api.com/plugin"
26 | runtime_config:
27 | allowed_hosts:
28 | - "internal.api.com"
29 | - "secure.vault.com"
30 | env_vars:
31 | KEYRING_SERVICE: "internal-api"
32 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | FROM --platform=$BUILDPLATFORM rust:1.90 AS builder
2 | WORKDIR /app
3 | RUN cargo install cargo-auditable
4 |
5 | COPY Cargo.toml Cargo.lock ./
6 | RUN cargo fetch
7 | COPY src ./src
8 | RUN cargo auditable build --release --locked
9 |
10 | FROM debian:13-slim
11 |
12 | LABEL org.opencontainers.image.authors="[email protected]" \
13 | org.opencontainers.image.url="https://github.com/tuananh/hyper-mcp" \
14 | org.opencontainers.image.source="https://github.com/tuananh/hyper-mcp" \
15 | org.opencontainers.image.vendor="github.com/tuananh/hyper-mcp" \
16 | io.modelcontextprotocol.server.name="io.github.tuananh/hyper-mcp"
17 |
18 | RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
19 |
20 | WORKDIR /app
21 | COPY --from=builder /app/target/release/hyper-mcp /usr/local/bin/hyper-mcp
22 | ENTRYPOINT ["/usr/local/bin/hyper-mcp"]
23 |
```
--------------------------------------------------------------------------------
/tests/fixtures/config_with_auths.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "auths": {
3 | "https://api.example.com": {
4 | "type": "basic",
5 | "username": "testuser",
6 | "password": "testpass"
7 | },
8 | "https://secure.api.com": {
9 | "type": "token",
10 | "token": "bearer-token-123"
11 | },
12 | "https://private.registry.io": {
13 | "type": "basic",
14 | "username": "admin",
15 | "password": "secretpass"
16 | },
17 | "https://oauth.service.com": {
18 | "type": "token",
19 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
20 | }
21 | },
22 | "plugins": {
23 | "test_plugin": {
24 | "url": "file:///path/to/plugin",
25 | "runtime_config": {
26 | "allowed_hosts": ["api.example.com", "secure.api.com"]
27 | }
28 | },
29 | "auth_test_plugin": {
30 | "url": "https://secure.api.com/plugin",
31 | "runtime_config": {
32 | "allowed_hosts": ["secure.api.com"]
33 | }
34 | }
35 | }
36 | }
37 |
```
--------------------------------------------------------------------------------
/tests/fixtures/documentation_example.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "auths": {
3 | "https://private.registry.io": {
4 | "type": "basic",
5 | "username": "registry-user",
6 | "password": "registry-pass"
7 | },
8 | "https://api.github.com": {
9 | "type": "token",
10 | "token": "ghp_1234567890abcdef"
11 | },
12 | "https://enterprise.api.com": {
13 | "type": "basic",
14 | "username": "enterprise-user",
15 | "password": "enterprise-pass"
16 | }
17 | },
18 | "plugins": {
19 | "time": {
20 | "url": "oci://ghcr.io/tuananh/time-plugin:latest"
21 | },
22 | "myip": {
23 | "url": "oci://ghcr.io/tuananh/myip-plugin:latest",
24 | "runtime_config": {
25 | "allowed_hosts": ["1.1.1.1"],
26 | "skip_tools": ["debug"],
27 | "env_vars": { "FOO": "bar" },
28 | "memory_limit": "512Mi"
29 | }
30 | },
31 | "private_plugin": {
32 | "url": "https://private.registry.io/my_plugin",
33 | "runtime_config": {
34 | "allowed_hosts": ["private.registry.io"]
35 | }
36 | }
37 | }
38 | }
39 |
```
--------------------------------------------------------------------------------
/src/wasm/http.rs:
--------------------------------------------------------------------------------
```rust
1 | use crate::{config::AuthConfig, https_auth::Authenticator};
2 | use anyhow::{Result, anyhow};
3 | use reqwest::Client;
4 | use std::collections::HashMap;
5 | use tokio::sync::OnceCell;
6 | use url::Url;
7 |
8 | static REQWEST_CLIENT: OnceCell<Client> = OnceCell::const_new();
9 |
10 | pub async fn load_wasm(url: &Url, auths: &Option<HashMap<Url, AuthConfig>>) -> Result<Vec<u8>> {
11 | match url.scheme() {
12 | "http" => Ok(REQWEST_CLIENT
13 | .get_or_init(|| async { reqwest::Client::new() })
14 | .await
15 | .get(url.as_str())
16 | .send()
17 | .await?
18 | .bytes()
19 | .await?
20 | .to_vec()),
21 | "https" => Ok(REQWEST_CLIENT
22 | .get_or_init(|| async { reqwest::Client::new() })
23 | .await
24 | .get(url.as_str())
25 | .add_auth(auths, url)
26 | .send()
27 | .await?
28 | .bytes()
29 | .await?
30 | .to_vec()),
31 | _ => Err(anyhow!("Unsupported URL scheme: {}", url.scheme())),
32 | }
33 | }
34 |
```
--------------------------------------------------------------------------------
/.github/renovate.json5:
--------------------------------------------------------------------------------
```
1 | {
2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json',
3 | extends: [
4 | 'config:recommended',
5 | ],
6 | schedule: [
7 | 'on monday',
8 | ],
9 | packageRules: [
10 | {
11 | matchDepTypes: [
12 | 'action',
13 | ],
14 | pinDigests: true,
15 | },
16 | {
17 | extends: [
18 | 'helpers:pinGitHubActionDigests',
19 | ],
20 | extractVersion: '^(?<version>v?\\d+\\.\\d+\\.\\d+)$',
21 | versioning: 'regex:^v?(?<major>\\d+)(\\.(?<minor>\\d+)\\.(?<patch>\\d+))?$',
22 | },
23 | {
24 | matchManagers: [
25 | 'github-actions',
26 | ],
27 | groupName: 'GitHub Actions',
28 | labels: [
29 | 'dependencies',
30 | 'github-actions',
31 | ],
32 | commitMessagePrefix: 'github-actions',
33 | "rangeStrategy": "pin",
34 | },
35 | {
36 | matchManagers: [
37 | 'cargo',
38 | ],
39 | groupName: 'Rust dependencies',
40 | labels: [
41 | 'dependencies',
42 | 'rust',
43 | ],
44 | commitMessagePrefix: 'rust',
45 | },
46 | ],
47 | prHourlyLimit: 4,
48 | prConcurrentLimit: 16,
49 | dependencyDashboard: true,
50 | }
51 |
```
--------------------------------------------------------------------------------
/config.example.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "plugins": {
3 | "time": {
4 | "url": "oci://ghcr.io/tuananh/time-plugin:latest"
5 | },
6 | "qr-code": {
7 | "url": "oci://ghcr.io/tuananh/qrcode-plugin:latest"
8 | },
9 | "hash": {
10 | "url": "oci://ghcr.io/tuananh/hash-plugin:latest"
11 | },
12 | "myip": {
13 | "url": "oci://ghcr.io/tuananh/myip-plugin:latest",
14 | "runtime_config": {
15 | "allowed_hosts": ["1.1.1.1"],
16 | "skip_tools": ["debug_.*", ".*_test"]
17 | }
18 | },
19 | "fetch": {
20 | "url": "oci://ghcr.io/tuananh/fetch-plugin:latest",
21 | "runtime_config": {
22 | "allowed_hosts": ["*"],
23 | "skip_tools": ["temp_.*", "mock_.*", "tool_[0-9]+"]
24 | }
25 | },
26 | "example_plugin": {
27 | "url": "oci://ghcr.io/example/demo-plugin:latest",
28 | "runtime_config": {
29 | "skip_tools": [
30 | "admin_tool",
31 | "dev_.*",
32 | ".*_deprecated",
33 | "test_(unit|integration)",
34 | "[a-z]+_helper"
35 | ],
36 | "allowed_hosts": ["example.com"],
37 | "memory_limit": "256Mi"
38 | }
39 | }
40 | }
41 | }
42 |
```
--------------------------------------------------------------------------------
/.windsurf/rules/think.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | trigger: model_decision
3 | ---
4 |
5 | After any context change (viewing new files, running commands, or receiving tool outputs), use the "think" tool to organize your reasoning before responding.
6 |
7 | Specifically, always use the think tool when:
8 | - After examining file contents or project structure
9 | - After running terminal commands or analyzing their outputs
10 | - After receiving search results or API responses
11 | - Before making code suggestions or explaining complex concepts
12 | - When transitioning between different parts of a task
13 |
14 | When using the think tool:
15 | - List the specific rules or constraints that apply to the current task
16 | - Check if all required information is collected
17 | - Verify that your planned approach is correct
18 | - Break down complex problems into clearly defined steps
19 | - Analyze outputs from other tools thoroughly
20 | - Plan multi-step approaches before executing them
21 |
22 | The think tool has been proven to improve performance by up to 54% on complex tasks, especially when working with multiple tools or following detailed policies.
23 |
```
--------------------------------------------------------------------------------
/src/wasm/s3.rs:
--------------------------------------------------------------------------------
```rust
1 | use anyhow::Result;
2 | use aws_sdk_s3::Client;
3 | use tokio::sync::OnceCell;
4 | use url::Url;
5 |
6 | static S3_CLIENT: OnceCell<Client> = OnceCell::const_new();
7 |
8 | pub async fn load_wasm(url: &Url) -> Result<Vec<u8>> {
9 | let bucket = url
10 | .host_str()
11 | .ok_or_else(|| anyhow::anyhow!("S3 URL must have a valid bucket name in the host"))?;
12 | let key = url.path().trim_start_matches('/');
13 | match S3_CLIENT
14 | .get_or_init(|| async { Client::new(&aws_config::load_from_env().await) })
15 | .await
16 | .get_object()
17 | .bucket(bucket)
18 | .key(key)
19 | .send()
20 | .await
21 | {
22 | Ok(response) => match response.body.collect().await {
23 | Ok(body) => Ok(body.to_vec()),
24 | Err(e) => {
25 | tracing::error!("Failed to collect S3 object body: {e}");
26 | Err(anyhow::anyhow!("Failed to collect S3 object body: {e}"))
27 | }
28 | },
29 | Err(e) => {
30 | tracing::error!("Failed to get object from S3: {e}");
31 | Err(anyhow::anyhow!("Failed to get object from S3: {e}"))
32 | }
33 | }
34 | }
35 |
```
--------------------------------------------------------------------------------
/config.example.yaml:
--------------------------------------------------------------------------------
```yaml
1 | ---
2 | plugins:
3 | time:
4 | url: oci://ghcr.io/tuananh/time-plugin:latest
5 | qr-code:
6 | url: oci://ghcr.io/tuananh/qrcode-plugin:latest
7 | hash:
8 | url: oci://ghcr.io/tuananh/hash-plugin:latest
9 | myip:
10 | url: oci://ghcr.io/tuananh/myip-plugin:latest
11 | runtime_config:
12 | allowed_hosts:
13 | - "1.1.1.1"
14 | skip_tools:
15 | - "debug_.*" # Skip all debug tools
16 | - ".*_test" # Skip all test tools
17 | fetch:
18 | url: oci://ghcr.io/tuananh/fetch-plugin:latest
19 | runtime_config:
20 | allowed_hosts:
21 | - "*"
22 | skip_tools:
23 | - "temp_.*" # Skip temporary tools
24 | - "mock_.*" # Skip mock tools
25 | - "tool_[0-9]+" # Skip numbered tools like "tool_1", "tool_42"
26 |
27 | # Example plugin with comprehensive skip_tools patterns
28 | example_plugin:
29 | url: oci://ghcr.io/example/demo-plugin:latest
30 | runtime_config:
31 | skip_tools:
32 | - "admin_tool" # Skip exact tool name
33 | - "dev_.*" # Skip development tools
34 | - ".*_deprecated" # Skip deprecated tools
35 | - "test_(unit|integration)" # Skip specific test types
36 | - "[a-z]+_helper" # Skip lowercase helpers
37 | allowed_hosts:
38 | - "example.com"
39 | memory_limit: "256Mi"
40 |
```