diff --git a/Cargo.toml b/Cargo.toml index 317e479b..2ad101b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] -members = ["crates/rmcp", "crates/rmcp-macros", "examples/*"] +members = [ "bindings/python", "bindings/typescript", "crates/rmcp", "crates/rmcp-macros", "examples/*"] resolver = "2" [workspace.dependencies] diff --git a/bindings/typescript/.gitignore b/bindings/typescript/.gitignore new file mode 100644 index 00000000..b599472e --- /dev/null +++ b/bindings/typescript/.gitignore @@ -0,0 +1,29 @@ +# TypeScript/Node ignores +node_modules/ +dist/ +build/ +*.tsbuildinfo +.env +.env.* +coverage/ + +# napi-rs ignores +*.node +index.node +*.d.ts +artifacts/ +.napi/ + +# Rust ignores +target/ +Cargo.lock + +# IDE ignores +.vscode/ +.idea/ +*.swp +*.swo + +# OS ignores +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/bindings/typescript/.placeholder b/bindings/typescript/.placeholder new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/bindings/typescript/.placeholder @@ -0,0 +1 @@ + diff --git a/bindings/typescript/Cargo.toml b/bindings/typescript/Cargo.toml new file mode 100644 index 00000000..dfad6207 --- /dev/null +++ b/bindings/typescript/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "rmcp_typescript" +edition.workspace = true +version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +description.workspace = true +keywords.workspace = true +homepage.workspace = true +categories.workspace = true +readme.workspace = true + +[dependencies] +rmcp = { path = "../../crates/rmcp", features = ["transport-sse", "client"] } +napi = { version = "2", features = ["serde-json", "tokio_rt"] } +napi-derive = "2" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +reqwest = { version = "0.12", features = ["json"] } +sse-stream = "0.1.3" +url = "2.4" +tokio = { version = "1.0", features = ["full"] } +tokio-util = { version = "0.7", features = ["full"] } +[lib] +crate-type = ["cdylib"] + +[build-dependencies] +napi-build = "2" diff --git a/bindings/typescript/README.md b/bindings/typescript/README.md new file mode 100644 index 00000000..b399fd19 --- /dev/null +++ b/bindings/typescript/README.md @@ -0,0 +1,52 @@ + # TypeScript Bindings Setup & Example Usage + +## Prerequisites +- Rust (latest stable, with cargo) +- Node.js (latest LTS, with npm) +- TypeScript (install via `npm install -g typescript`) + +## Build the TypeScript Bindings +1. Navigate to the TypeScript bindings directory: + ```bash + cd bindings/typescript + ``` +2. Install dependencies and build the project: + ```bash + npm install + npm run build + ``` +## Note on Cargo.toml +If you encounter an error indicating that the `Cargo.toml` of `bindings/python` is not present in the root `Cargo.toml`, and you don't want to build Python and TypeScript together, you can edit the root `Cargo.toml` to remove the Python bindings (bindings/python) entry. + +## Run the Rust Server +In a separate terminal, start the Rust SDK server (example using the rmcp crate): +```bash +cd examples/servers +cargo run --example servers_axum +``` + +## Link the Module Locally +If the `rmcp-typescript` module is not published to npm, you can link it locally: +1. Navigate to the TypeScript bindings directory: + ```bash + cd bindings/typescript + ``` +2. Link the module: + ```bash + npm link + ``` +3. Navigate to the examples/clients directory and link the module: + ```bash + cd examples/clients + npm link rmcp-typescript + ``` + +## Run the TypeScript SSE Client Example +With the server running, in another terminal: +```bash +cd bindings/typescript/examples/clients/src +npx tsx sse.ts +``` + +This will connect to the running Rust server and demonstrate the SSE client functionality. + diff --git a/bindings/typescript/build.rs b/bindings/typescript/build.rs new file mode 100644 index 00000000..0f1b0100 --- /dev/null +++ b/bindings/typescript/build.rs @@ -0,0 +1,3 @@ +fn main() { + napi_build::setup(); +} diff --git a/bindings/typescript/examples/clients/package-lock.json b/bindings/typescript/examples/clients/package-lock.json new file mode 100644 index 00000000..129796cb --- /dev/null +++ b/bindings/typescript/examples/clients/package-lock.json @@ -0,0 +1,777 @@ +{ + "name": "rmcp-typescript-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rmcp-typescript-example", + "version": "1.0.0", + "dependencies": { + "rmcp-typescript": "file:../.." + }, + "devDependencies": { + "@types/node": "^22.15.18", + "ts-node": "^10.9.2", + "tsx": "^4.19.4", + "typescript": "^5.8.3" + } + }, + "../..": { + "name": "rmcp-typescript", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.15.18", + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", + "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rmcp-typescript": { + "resolved": "../..", + "link": true + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsx": { + "version": "4.19.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", + "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/bindings/typescript/examples/clients/package.json b/bindings/typescript/examples/clients/package.json new file mode 100644 index 00000000..bb937a13 --- /dev/null +++ b/bindings/typescript/examples/clients/package.json @@ -0,0 +1,17 @@ +{ + "name": "rmcp-typescript-example", + "version": "1.0.0", + "type": "commonjs", + "scripts": { + "start": "ts-node-esm src/sse.ts" + }, + "dependencies": { + "rmcp-typescript": "file:../.." + }, + "devDependencies": { + "@types/node": "^22.15.18", + "ts-node": "^10.9.2", + "tsx": "^4.19.4", + "typescript": "^5.8.3" + } +} diff --git a/bindings/typescript/examples/clients/src/sse.ts b/bindings/typescript/examples/clients/src/sse.ts new file mode 100644 index 00000000..0fa69de2 --- /dev/null +++ b/bindings/typescript/examples/clients/src/sse.ts @@ -0,0 +1,59 @@ +// TypeScript SSE client example for rmcp_typescript +// Closely mirrors the structure and flow of sse.rs from the core Rust SDK + +import { JsTransport, JsSseTransport, JsClientInfo, JsImplementation, JsClientCapabilities, JsExperimentalCapabilities, JsRootsCapabilities, JsClient } from 'rmcp-typescript'; + +// TODO: Replace 'any' with proper types from WASM/SDK when available +// type ClientCapabilities = any; +// type CallToolRequestParam = { name: string; arguments?: object }; + +async function main() { + // Step 1: Initialize logging (console-based for now) + console.info('Starting TypeScript SSE client example'); + + // Step 2: Create transport + const sseEndpoint = 'http://localhost:8000/sse'; + const sseTransport = await JsSseTransport.start(sseEndpoint); + console.log('sseTransport:', sseTransport); + const transport = JsTransport.fromSse(sseTransport); + console.log('transport:', transport.kind); + + // Step 3: Define client info (mirror Rust structure) + const experimental = JsExperimentalCapabilities.new({}); + const roots = new JsRootsCapabilities(); + const sampling = null; + console.log('JsClientCapabilities.new args:', { experimental, roots, sampling }); + const clientInfo = new JsClientInfo( + '2024-11-05', + new JsClientCapabilities(experimental, roots, sampling), + new JsImplementation('typescript-sse-client', '0.0.1') + ); + + try { + // Step 4: Start the client and get peer info + const clientObj = await clientInfo.serve(transport); + const client = clientObj.inner as JsClient; + const serverInfo = client.peerInfo(); + console.info('Connected to server:', serverInfo); + + // Step 5: List available tools + const tools = await client.listAllTools(); + console.info('Available tools:', tools); + + // Step 6: Call a tool (example) + + const result = await client.callTool("increment", {}); + console.info('Tool result:', result); + + + // Keep connection open for demonstration + setTimeout(() => { + console.info('Connection closed.'); + }, 10000); + + } catch (e) { + console.error('Client error:', e); + } +} + +main(); diff --git a/bindings/typescript/examples/clients/tsconfig.json b/bindings/typescript/examples/clients/tsconfig.json new file mode 100644 index 00000000..7ae69173 --- /dev/null +++ b/bindings/typescript/examples/clients/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "allowJs": true, + "types": ["node"] + }, + "ts-node": { + "esm": true, + "experimentalSpecifierResolution": "node" + } +} \ No newline at end of file diff --git a/bindings/typescript/package-lock.json b/bindings/typescript/package-lock.json new file mode 100644 index 00000000..b3005e49 --- /dev/null +++ b/bindings/typescript/package-lock.json @@ -0,0 +1,1152 @@ +{ + "name": "rmcp-typescript", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "rmcp-typescript", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.15.18", + "ts-node": "^10.9.2", + "tsx": "^4.19.4", + "typescript": "^5.8.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", + "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsx": { + "version": "4.19.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", + "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "dev": true, + "optional": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/node": { + "version": "22.15.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", + "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "dev": true, + "requires": { + "undici-types": "~6.21.0" + } + }, + "acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true + }, + "acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "requires": { + "acorn": "^8.11.0" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, + "ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tsx": { + "version": "4.19.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", + "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", + "dev": true, + "requires": { + "esbuild": "~0.25.0", + "fsevents": "~2.3.3", + "get-tsconfig": "^4.7.5" + } + }, + "typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true + }, + "undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/bindings/typescript/package.json b/bindings/typescript/package.json new file mode 100644 index 00000000..71b8cc61 --- /dev/null +++ b/bindings/typescript/package.json @@ -0,0 +1,18 @@ +{ + "name": "rmcp-typescript", + "version": "0.1.0", + "description": "TypeScript/NodeJS native binding for the rmcp Rust SDK (napi-rs)", + "main": "./index.js", + "types": "./index.d.ts", + "scripts": { + "build": "napi build --release", + "test": "tsc && node tests/add.test.js" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.15.18", + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + } +} diff --git a/bindings/typescript/rmcp_typescript/index.ts b/bindings/typescript/rmcp_typescript/index.ts new file mode 100644 index 00000000..6cc20a21 --- /dev/null +++ b/bindings/typescript/rmcp_typescript/index.ts @@ -0,0 +1,76 @@ +/** + * ClientInfo represents information about the client, such as protocol version, capabilities, and implementation details. + * This structure should mirror the Rust/Python SDK structure and will eventually be backed by WASM bindings. + */ +export interface ClientInfo { + protocolVersion: string; + capabilities: object; // Should be typed to ClientCapabilities + clientInfo: Implementation; +} + +/** + * Implementation details for the client, such as name and version. + * This structure should mirror the Rust/Python SDK Implementation struct. + */ +export interface Implementation { + name: string; + version: string; +} + +/** + * SSETransport provides an interface for connecting to an SSE (Server-Sent Events) endpoint. + * In the browser, this would use EventSource; in Node.js, a compatible polyfill or HTTP client. + * In the future, this should be backed by Rust/WASM for protocol logic. + */ +export class SSETransport { + private url: string; + private eventSource: EventSource | null = null; + + /** + * Create a new SSETransport for the given URL. + * @param url The SSE endpoint URL. + */ + constructor(url: string) { + this.url = url; + } + + /** + * Start the SSE connection. In browser, uses EventSource. In Node, requires a polyfill. + * @param onMessage Callback for each message event. + * @param onError Callback for error events. + */ + start(onMessage: (data: any) => void, onError?: (err: any) => void) { + if (typeof window !== 'undefined' && typeof window.EventSource !== 'undefined') { + this.eventSource = new window.EventSource(this.url); + this.eventSource.onmessage = (event) => { + onMessage(event.data); + }; + if (onError) { + this.eventSource.onerror = onError; + } + } else { + // Node.js: User must provide a compatible EventSource polyfill + throw new Error('SSETransport requires EventSource (browser) or a polyfill (Node.js)'); + } + } + + /** + * Close the SSE connection. + */ + close() { + if (this.eventSource) { + this.eventSource.close(); + this.eventSource = null; + } + } +} + +/** + * IntoTransport is an interface for types that can be converted into a transport. + * This is a placeholder for extensibility and should mirror the Rust trait. + */ +export interface IntoTransport { + intoTransport(): SSETransport; +} + +// TODO: When Rust/WASM bindings are available, replace these stubs with real implementations and types. diff --git a/bindings/typescript/src/client.rs b/bindings/typescript/src/client.rs new file mode 100644 index 00000000..24e70da6 --- /dev/null +++ b/bindings/typescript/src/client.rs @@ -0,0 +1,13 @@ +//! Client interface for rmcp_typescript binding. +//! +//! This module provides the client-side entry point for interacting with the RMCP SDK from TypeScript/JavaScript. +//! It is intended to be used with the `napi` bindings for Node.js interoperability. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! import { Client } from 'rmcp-typescript'; +//! const client = new Client(); +//! // ... +//! ``` +// Add #[napi] wrappers as needed for TS/JS diff --git a/bindings/typescript/src/lib.rs b/bindings/typescript/src/lib.rs new file mode 100644 index 00000000..952520a3 --- /dev/null +++ b/bindings/typescript/src/lib.rs @@ -0,0 +1,71 @@ +//! # rmcp_typescript +//! +//! TypeScript/JavaScript bindings for the RMCP SDK, powered by napi-rs. +//! +//! This crate exposes a Node.js-compatible API for interacting with RMCP services from TypeScript or JavaScript. +//! +//! ## Usage Example (TypeScript) +//! +//! ```typescript +//! import { +//! JsTransport, +//! JsSseTransport, +//! JsClientInfo, +//! JsImplementation, +//! JsClientCapabilities, +//! JsExperimentalCapabilities, +//! JsRootsCapabilities, +//! } from 'rmcp-typescript'; +//! +//! // 1. Create an SSE transport +//! const sseEndpoint = 'http://localhost:8000/sse'; +//! const sseTransport = await JsSseTransport.start(sseEndpoint); +//! const transport = JsTransport.fromSse(sseTransport); +//! +//! // 2. Define client info and capabilities +//! const experimental = JsExperimentalCapabilities.new({}); +//! const roots = new JsRootsCapabilities(); +//! const capabilities = new JsClientCapabilities(experimental, roots, null); +//! const impl = new JsImplementation('typescript-client', '0.1.0'); +//! const clientInfo = new JsClientInfo('2.0', capabilities, impl); +//! +//! // 3. Serve the RMCP client +//! const clientObj = await clientInfo.serve(transport); +//! const client = clientObj.inner; +//! +//! // 4. Interact with the server +//! const info = await client.peerInfo(); +//! console.log('Connected to server:', info); +//! const tools = await client.listAllTools(); +//! console.log('Available tools:', tools); +//! const result = await client.callTool('increment', { value: 1 }); +//! console.log('Tool result:', result); +//! ``` +//! +//! For more details, see the documentation for each module and struct. + +pub mod model; +pub mod client; +pub mod service; +pub mod transport; + +// Re-export capability types for WASM/TS bindings +pub use model::capabilities::{ + JsPromptsCapability, + JsResourcesCapability, + JsToolsCapability, + JsRootsCapabilities, + JsExperimentalCapabilities, + JsClientCapabilities, +}; + +// Re-export model types +pub use model::{ + JsClientInfo, + JsImplementation, + ProtocolVersion, +}; + +// Re-export service types +pub use service::JsClient; + diff --git a/bindings/typescript/src/model.rs b/bindings/typescript/src/model.rs new file mode 100644 index 00000000..b713ad86 --- /dev/null +++ b/bindings/typescript/src/model.rs @@ -0,0 +1,400 @@ +//! Model types for rmcp_typescript binding. +//! +//! This module defines core types (client info, implementation info, protocol version, etc.) exposed to TypeScript/JavaScript via napi bindings. +//! These types are used for configuring and interacting with the RMCP SDK from Node.js or browser environments. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! import { JsClientInfo, JsImplementation, ProtocolVersion } from 'rmcp-typescript'; +//! const info = new JsClientInfo('2.0', capabilities, impl, env); +//! ``` + +pub mod tool; +pub mod resource; +pub mod prompt; +pub mod prompt_message; +pub mod capabilities; + +use napi::bindgen_prelude::*; +use napi::JsObject; +use napi::NapiValue; +use napi_derive::napi; +use crate::model::capabilities::JsClientCapabilities; +use serde_json; +use crate::service::JsClient; +use crate::transport::{JsTransport, JsTransportInner}; +use std::sync::Arc; +use rmcp::model::ClientInfo; +use rmcp::ServiceExt; + + +/// Client info for establishing a connection to an RMCP server. +/// +/// Used to specify protocol version, capabilities, and implementation details. +/// +/// # Example (TypeScript) +/// ```typescript +/// const info = new JsClientInfo('2.0', capabilities, impl, env); +/// ``` +#[napi] +#[derive(Clone)] +pub struct JsClientInfo { + #[napi(skip)] + pub inner: ClientInfo, +} + +#[napi] +impl JsClientInfo { + /// Construct a new `JsClientInfo`. + /// + /// # Arguments + /// * `protocol_version` - The protocol version string (e.g. "2.0"). + /// * `capabilities` - Reference to client capabilities. + /// * `client_info` - Reference to implementation info. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const info = new JsClientInfo('2.0', capabilities, impl); + /// ``` + #[napi(constructor)] + pub fn new(protocol_version: String, capabilities: Reference, client_info: Reference, env: Env) -> napi::Result { + println!("JsClientInfo::new received protocol_version: {}", protocol_version); + println!("JsClientInfo::new received capabilities: valid"); + println!("JsClientInfo::new received client_info: valid"); + let protocol_version = serde_json::from_str(&format!("\"{}\"", protocol_version)) + .map_err(|e| napi::Error::from_reason(e.to_string()))?; + println!("JsClientInfo::new parsed protocol_version: {:?}", protocol_version); + let capabilities = capabilities.clone(env)?.to_rust(); + println!("JsClientInfo::new capabilities after clone: {:?}", capabilities); + let client_info = client_info.clone(env)?.to_rust(); + println!("JsClientInfo::new client_info after clone: {:?}", client_info); + + let inner = rmcp::model::ClientInfo { + protocol_version, + capabilities, + client_info, + }; + Ok(JsClientInfo { inner }) + } + + /// Get the protocol version for this client info. + #[napi(getter)] + pub fn protocol_version(&self) -> String { + format!("{:?}", self.inner.protocol_version) + } + + /// Get the client capabilities for this client info. + #[napi(getter)] + pub fn capabilities(&self) -> JsClientCapabilities { + JsClientCapabilities::from_rust(&self.inner.capabilities) + } + + /// Get the implementation info for this client info. + #[napi(getter)] + pub fn client_info(&self) -> JsImplementation { + JsImplementation::from_rust(&self.inner.client_info) + } + + /// Serve the RMCP client using the provided transport. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const client = await info.serve(transport); + /// ``` + #[napi] + pub fn serve(&self, transport: Reference, env: Env) -> napi::Result { + let cloned_transport = transport.clone(env)?; + println!("JsClientInfo::serve cloned_transport: {:?}", cloned_transport.kind); + let this = self.clone(); + + let result = napi::bindgen_prelude::execute_tokio_future( + env.raw(), + async move { + this.serve_inner(cloned_transport).await + }, + |raw_env, client| { + let env = unsafe { Env::from_raw(raw_env) }; + let mut obj = env.create_object()?; + obj.set_named_property("inner", client)?; + unsafe { napi::bindgen_prelude::ToNapiValue::to_napi_value(raw_env, obj) } + } + )?; + + Ok(unsafe { JsObject::from_raw(env.raw(), result)? }) + } +} + +impl JsClientInfo { + pub fn to_rust(&self) -> rmcp::model::ClientInfo { + self.inner.clone() + } + pub fn from_rust(core: &rmcp::model::ClientInfo) -> JsClientInfo { + JsClientInfo { + inner: core.clone(), + } + } + + async fn serve_inner(&self, mut transport: Reference) -> napi::Result { + println!("JsClientInfo::serve_inner starting"); + let info = self.inner.clone(); + + match transport.inner.take() { + Some(JsTransportInner::Tcp(stream)) => { + println!("JsClientInfo::serve_inner received TCP stream"); + match stream { + Some(stream) => { + println!("JsClientInfo::serve_inner TCP stream is Some"); + let running = info.serve(stream).await + .map_err(|e| napi::Error::from_reason(format!("TCP Serve IO error: {}", e)))?; + let peer = running.peer().clone(); + let peer_arc = Arc::new(peer); + let mut js_client = JsClient::new(); + js_client.set_inner(peer_arc); + Ok(js_client) + } + None => { + println!("JsClientInfo::serve_inner TCP stream is None"); + Err(napi::Error::from_reason("TCP stream not initialized")) + } + } + } + Some(JsTransportInner::Stdio(stdin_stdout)) => { + println!("JsClientInfo::serve_inner received STDIO"); + match stdin_stdout { + Some((stdin, stdout)) => { + println!("JsClientInfo::serve_inner STDIO is Some"); + let running = info.serve((stdin, stdout)).await + .map_err(|e| napi::Error::from_reason(format!("STDIO Serve IO error: {}", e)))?; + let peer = running.peer().clone(); + let peer_arc = Arc::new(peer); + let mut js_client = JsClient::new(); + js_client.set_inner(peer_arc); + Ok(js_client) + } + None => { + println!("JsClientInfo::serve_inner STDIO is None"); + Err(napi::Error::from_reason("STDIO not initialized")) + } + } + } + Some(JsTransportInner::Sse(sse)) => { + println!("JsClientInfo::serve_inner received SSE"); + match sse { + Some(sse) => { + println!("JsClientInfo::serve_inner SSE is Some"); + let running = info.serve(sse).await + .map_err(|e| napi::Error::from_reason(format!("SSE Serve IO error: {}", e)))?; + let peer = running.peer().clone(); + let peer_arc = Arc::new(peer); + let mut js_client = JsClient::new(); + js_client.set_inner(peer_arc); + Ok(js_client) + } + None => { + println!("JsClientInfo::serve_inner SSE is None"); + Err(napi::Error::from_reason("SSE transport not initialized")) + } + } + } + None => { + println!("JsClientInfo::serve_inner transport inner is None"); + Err(napi::Error::from_reason("Transport not initialized")) + } + } + } +} + +/// Implementation info for the RMCP client (name and version). +/// +/// # Example (TypeScript) +/// ```typescript +/// const impl = new JsImplementation('my-client', '1.0.0'); +/// ``` +#[napi] +#[derive(Clone, Debug, PartialEq)] +pub struct JsImplementation { + pub name: String, + pub version: String, +} + +#[napi] +impl JsImplementation { + /// Construct a new implementation info object. + /// + /// # Arguments + /// * `name` - Name of the implementation. + /// * `version` - Version string. + /// + /// # Example (TypeScript) + /// ```typescript + /// const impl = new JsImplementation('my-client', '1.0.0'); + /// ``` + #[napi(constructor)] + pub fn new(name: String, version: String) -> JsImplementation { + println!("JsImplementation::new received name: {}", name); + println!("JsImplementation::new received version: {}", version); + JsImplementation { name, version } + } + + #[napi(getter)] + pub fn name(&self) -> String { + self.name.clone() + } + + #[napi(getter)] + pub fn version(&self) -> String { + self.version.clone() + } +} + +impl Default for JsImplementation { + fn default() -> Self { + JsImplementation { + name: env!("CARGO_CRATE_NAME").to_owned(), + version: env!("CARGO_PKG_VERSION").to_owned(), + } + } +} + +impl JsImplementation { + pub fn to_rust(&self) -> rmcp::model::Implementation { + rmcp::model::Implementation { + name: self.name.clone(), + version: self.version.clone(), + } + } + pub fn from_rust(core: &rmcp::model::Implementation) -> JsImplementation { + JsImplementation { + name: core.name.clone(), + version: core.version.clone(), + } + } +} + +/// Protocol version wrapper for RMCP. +/// +/// # Example (TypeScript) +/// ```typescript +/// const version = new ProtocolVersion('2.0'); +/// ``` +#[napi] +#[derive(Clone)] +pub struct ProtocolVersion { + pub value: String, +} + +#[napi] +impl ProtocolVersion { + /// Construct a new protocol version. + /// + /// # Arguments + /// * `value` - Version string (e.g. "2.0"). + /// + /// # Example (TypeScript) + /// ```typescript + /// const version = new ProtocolVersion('2.0'); + /// ``` + #[napi(constructor)] + pub fn new(value: String) -> ProtocolVersion { + ProtocolVersion { value } + } + + /// Get the protocol version value. + #[napi(getter)] + pub fn value(&self) -> String { + self.value.clone() + } + + /// Convert this protocol version to a string. + #[napi(js_name = "toString")] + pub fn to_string(&self) -> String { + self.value.clone() + } + + /// Get the latest supported protocol version. + #[napi] + pub fn latest() -> ProtocolVersion { + ProtocolVersion { value: "2.0".to_string() } + } +} + +#[napi] +pub fn json_rpc_version_2_0() -> String { + "2.0".to_string() +} + +/// Information about the RMCP server or client. +/// +/// # Example (TypeScript) +/// ```typescript +/// const info = new JsInfo('2.0', 'my-server', '1.0.0'); +/// ``` +#[napi] +#[derive(Clone)] +pub struct JsInfo { + pub protocol_version: String, + pub name: String, + pub version: String, +} + +#[napi] +impl JsInfo { + /// Construct a new info object. + /// + /// # Arguments + /// * `protocol_version` - Protocol version string. + /// * `name` - Name of the server/client. + /// * `version` - Version string. + /// + /// # Example (TypeScript) + /// ```typescript + /// const info = new JsInfo('2.0', 'my-server', '1.0.0'); + /// ``` + #[napi(constructor)] + pub fn new(protocol_version: String, name: String, version: String) -> JsInfo { + JsInfo { + protocol_version, + name, + version, + } + } + + /// Get the protocol version for this info. + #[napi(getter)] + pub fn protocol_version(&self) -> String { + self.protocol_version.clone() + } + + #[napi(getter)] + pub fn name(&self) -> String { + self.name.clone() + } + + #[napi(getter)] + pub fn version(&self) -> String { + self.version.clone() + } +} + +impl JsInfo { + pub fn server(info: rmcp::model::InitializeResult) -> Self { + Self { + protocol_version: format!("{:?}", info.protocol_version), + name: info.server_info.name, + version: info.server_info.version, + } + } + + pub fn client(param: rmcp::model::InitializeRequestParam) -> Self { + Self { + protocol_version: format!("{:?}", param.protocol_version), + name: param.client_info.name, + version: param.client_info.version, + } + } +} diff --git a/bindings/typescript/src/model/capabilities.rs b/bindings/typescript/src/model/capabilities.rs new file mode 100644 index 00000000..532854c0 --- /dev/null +++ b/bindings/typescript/src/model/capabilities.rs @@ -0,0 +1,168 @@ +use std::collections::BTreeMap; +use napi::bindgen_prelude::*; +use napi_derive::napi; +use serde_json; +use rmcp::model::{ClientCapabilities, RootsCapabilities}; + +#[napi(object)] +pub struct JsPromptsCapability { + pub list_changed: Option, +} + +#[napi(object)] +pub struct JsResourcesCapability { + pub subscribe: Option, + pub list_changed: Option, +} + +#[napi(object)] +pub struct JsToolsCapability { + pub list_changed: Option, +} + +#[napi] +#[derive(Default, Clone, Debug, PartialEq)] +pub struct JsRootsCapabilities { + pub list_changed: Option, +} + +#[napi] +impl JsRootsCapabilities { + /// Construct a new roots capabilities object. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const roots = new JsRootsCapabilities(); + /// ``` + #[napi(constructor)] + pub fn new() -> JsRootsCapabilities { + JsRootsCapabilities { + list_changed: None, + } + } + + pub fn to_rust(&self) -> RootsCapabilities { + RootsCapabilities { + list_changed: self.list_changed, + } + } + pub fn from_rust(core: &RootsCapabilities) -> JsRootsCapabilities { + JsRootsCapabilities { + list_changed: core.list_changed, + } + } +} + +impl FromNapiValue for JsRootsCapabilities { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + unsafe { + let obj = Object::from_napi_value(env, napi_val)?; + let list_changed = obj.get("list_changed")?; + Ok(JsRootsCapabilities { list_changed }) + } + } +} + +#[napi] +#[derive(Default, Clone, Debug, PartialEq)] +pub struct JsExperimentalCapabilities { + #[napi(skip)] + pub inner: BTreeMap>, +} + +#[napi] +impl JsExperimentalCapabilities { + /// Construct new experimental capabilities from a JSON object. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const experimental = JsExperimentalCapabilities.new({}); + /// ``` + #[napi(factory, ts_type = "Record>")] + pub fn new(value: serde_json::Value) -> JsExperimentalCapabilities { + let inner: BTreeMap> = serde_json::from_value(value).unwrap_or_default(); + JsExperimentalCapabilities { inner } + } + pub fn to_json(&self) -> serde_json::Value { + serde_json::to_value(&self.inner).unwrap_or(serde_json::Value::Null) + } + pub fn from_js(value: serde_json::Value) -> JsExperimentalCapabilities { + let inner: BTreeMap> = serde_json::from_value(value).unwrap_or_default(); + JsExperimentalCapabilities { inner } + } +} + +impl JsExperimentalCapabilities { + pub fn to_rust(&self) -> BTreeMap> { + self.inner.clone() + } + pub fn from_rust(core: &BTreeMap>) -> JsExperimentalCapabilities { + JsExperimentalCapabilities { inner: core.clone() } + } +} + +impl FromNapiValue for JsExperimentalCapabilities { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + unsafe { + let value = serde_json::Value::from_napi_value(env, napi_val)?; + Ok(Self::from_js(value)) + } + } +} + +#[napi] +#[derive(Clone, Debug, PartialEq)] +pub struct JsClientCapabilities { + pub experimental: JsExperimentalCapabilities, + pub roots: JsRootsCapabilities, + pub sampling: Option, +} + +#[napi] +impl JsClientCapabilities { + /// Construct a new client capabilities object. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const capabilities = new JsClientCapabilities(experimental, roots, null); + /// ``` + #[napi(constructor)] + pub fn new( + experimental: JsExperimentalCapabilities, + roots: JsRootsCapabilities, + sampling: Option, + ) -> JsClientCapabilities { + println!("JsClientCapabilities::new received experimental: {:?}", experimental); + println!("JsClientCapabilities::new received roots: {:?}", roots); + println!("JsClientCapabilities::new received sampling: {:?}", sampling); + JsClientCapabilities { experimental, roots, sampling } + } +} + +impl JsClientCapabilities { + pub fn to_rust(&self) -> ClientCapabilities { + let sampling = match &self.sampling { + Some(v) => serde_json::from_value(v.clone()).ok(), + None => None, + }; + ClientCapabilities { + experimental: Some(self.experimental.to_rust()), + roots: Some(self.roots.to_rust()), + sampling, + } + } + + pub fn from_rust(core: &ClientCapabilities) -> JsClientCapabilities { + JsClientCapabilities { + experimental: core.experimental.as_ref().map(JsExperimentalCapabilities::from_rust).unwrap_or_default(), + roots: core.roots.as_ref().map(JsRootsCapabilities::from_rust).unwrap_or_default(), + sampling: core.sampling.as_ref().map(|v| serde_json::to_value(v).unwrap_or(serde_json::Value::Null)), + } + } +} diff --git a/bindings/typescript/src/model/prompt.rs b/bindings/typescript/src/model/prompt.rs new file mode 100644 index 00000000..e1a8db9d --- /dev/null +++ b/bindings/typescript/src/model/prompt.rs @@ -0,0 +1,12 @@ +//! Prompt types for rmcp_typescript binding. +//! +//! This module defines prompt-related types for RMCP, exposed to TypeScript/JavaScript via napi bindings. +//! Used for constructing and handling prompts in the RMCP SDK from Node.js or browser environments. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! // import { JsPrompt } from 'rmcp-typescript'; +//! // const prompt = new JsPrompt(...); +//! ``` +// Placeholder for prompt model diff --git a/bindings/typescript/src/model/prompt_message.rs b/bindings/typescript/src/model/prompt_message.rs new file mode 100644 index 00000000..96997b4c --- /dev/null +++ b/bindings/typescript/src/model/prompt_message.rs @@ -0,0 +1,12 @@ +//! Prompt message types for rmcp_typescript binding. +//! +//! This module defines prompt message types for RMCP, exposed to TypeScript/JavaScript via napi bindings. +//! Used for constructing and handling prompt messages in the RMCP SDK from Node.js or browser environments. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! // import { JsPromptMessage } from 'rmcp-typescript'; +//! // const msg = new JsPromptMessage(...); +//! ``` +// Placeholder for prompt_message model diff --git a/bindings/typescript/src/model/resource.rs b/bindings/typescript/src/model/resource.rs new file mode 100644 index 00000000..9561dceb --- /dev/null +++ b/bindings/typescript/src/model/resource.rs @@ -0,0 +1,12 @@ +//! Resource types for rmcp_typescript binding. +//! +//! This module defines resource types for RMCP, exposed to TypeScript/JavaScript via napi bindings. +//! Used for constructing and handling resources in the RMCP SDK from Node.js or browser environments. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! // import { JsResource } from 'rmcp-typescript'; +//! // const resource = new JsResource(...); +//! ``` +// Placeholder for resource model diff --git a/bindings/typescript/src/model/tool.rs b/bindings/typescript/src/model/tool.rs new file mode 100644 index 00000000..68d86bbd --- /dev/null +++ b/bindings/typescript/src/model/tool.rs @@ -0,0 +1,12 @@ +//! Tool types for rmcp_typescript binding. +//! +//! This module defines tool types for RMCP, exposed to TypeScript/JavaScript via napi bindings. +//! Used for constructing and handling tools in the RMCP SDK from Node.js or browser environments. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! // import { JsTool } from 'rmcp-typescript'; +//! // const tool = new JsTool(...); +//! ``` +// Placeholder for tool model diff --git a/bindings/typescript/src/service.rs b/bindings/typescript/src/service.rs new file mode 100644 index 00000000..e80ef7e3 --- /dev/null +++ b/bindings/typescript/src/service.rs @@ -0,0 +1,125 @@ +//! Service interface for rmcp_typescript binding. +//! +//! This module provides the main service interface (`JsClient`) for interacting with the RMCP SDK from TypeScript/JavaScript. +//! It exposes methods for retrieving server info, listing tools, and invoking tools, and is designed for use with Node.js via `napi` bindings. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! import { JsClient } from 'rmcp-typescript'; +//! const client = new JsClient(); +//! // await client.listAllTools(); +//! // await client.callTool('toolName', { ... }); +//! ``` + + +use napi_derive::napi; +use serde_json; +use rmcp::service::RoleClient; +use rmcp::service::Peer; +use std::sync::Arc; +use crate::model::{JsInfo}; +use rmcp::model::CallToolRequestParam; + +/// Main client object for interacting with RMCP services from TypeScript/JavaScript. +/// +/// Provides methods for retrieving server info, listing tools, and invoking tools. +#[napi] +#[derive(Clone)] +pub struct JsClient { + #[napi(skip)] + pub inner: Option>>, +} + +#[napi] +impl JsClient { + #[napi(constructor)] + pub fn new() -> JsClient { + JsClient { inner: None } + } + + /// Set the internal peer for this client (used internally after connecting). + pub fn set_inner(&mut self, inner: Arc>) { + self.inner = Some(inner); + } + + #[napi] + /// Retrieve information about the connected server. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const info = await client.peerInfo(); + /// console.log(info.name, info.version); + /// ``` + pub fn peer_info(&self) -> napi::Result { + match &self.inner { + Some(peer) => { + let info = peer.peer_info(); + Ok(JsInfo::server((*info).clone())) + } + None => Err(napi::Error::from_reason("Peer not initialized")), + } + } + + #[napi] + /// List all available tools on the server. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const tools = await client.listAllTools(); + /// console.log(tools); + /// ``` + pub async fn list_all_tools(&self) -> napi::Result { + match &self.inner { + Some(peer) => { + match peer.list_all_tools().await { + Ok(tools) => { + let tools_json = serde_json::to_value(tools) + .map_err(|e| napi::Error::from_reason(e.to_string()))?; + Ok(tools_json) + } + Err(e) => Err(napi::Error::from_reason(e.to_string())), + } + } + None => Err(napi::Error::from_reason("Peer not initialized")), + } + } + + #[napi] + /// Invoke a tool by name on the server. + /// + /// # Arguments + /// * `name` - The tool name to invoke. + /// * `arguments` - Optional arguments for the tool as a JSON object. + /// + /// > **Note:** Any extra parameters (such as environment/context) are injected by napi and should NOT be passed by the TypeScript user. + /// + /// # Example (TypeScript) + /// ```typescript + /// const result = await client.callTool('increment', { value: 1 }); + /// console.log(result); + /// ``` + pub async fn call_tool(&self, name: String, arguments: Option) -> napi::Result { + match &self.inner { + Some(peer) => { + let param = CallToolRequestParam { + name: name.into(), + arguments: arguments.and_then(|v| v.as_object().cloned()), + }; + match peer.call_tool(param).await { + Ok(result) => { + let result_json = serde_json::to_value(result) + .map_err(|e| napi::Error::from_reason(e.to_string()))?; + Ok(result_json) + } + Err(e) => Err(napi::Error::from_reason(e.to_string())), + } + } + None => Err(napi::Error::from_reason("Peer not initialized")), + } + } +} diff --git a/bindings/typescript/src/transport.rs b/bindings/typescript/src/transport.rs new file mode 100644 index 00000000..a519f490 --- /dev/null +++ b/bindings/typescript/src/transport.rs @@ -0,0 +1,218 @@ +//! Transport layer for rmcp_typescript binding. +//! +//! This module provides transport types and utilities for connecting to RMCP servers from TypeScript/JavaScript. +//! It supports TCP, stdio, and SSE transports, and is designed for use with Node.js via `napi` bindings. +//! +//! # Example (TypeScript) +//! +//! ```typescript +//! import { JsTransport } from 'rmcp-typescript'; +//! const tcpTransport = JsTransport.fromTcp(); +//! const sseTransport = JsTransport.fromSse(sseInstance); +//! ``` + +pub mod sse; + +use std::pin::Pin; +use tokio::net::TcpStream; +use tokio::io::{Stdin, Stdout}; +use napi::bindgen_prelude::*; +use napi_derive::napi; +use napi::Env; +use crate::transport::sse::JsSseTransport; +use rmcp::transport::sse::ReqwestSseClient; +use rmcp::transport::SseTransport; +use reqwest; + + +/// Enum representing the type of transport used to connect to the RMCP server. +/// +/// - `Tcp`: TCP socket transport +/// - `Stdio`: Standard input/output transport +/// - `Sse`: Server-sent events transport +/// +/// # Example (TypeScript) +/// ```typescript +/// if (transport.kind === 'Sse') { ... } +/// ``` +#[napi(string_enum)] +#[derive(PartialEq, Debug)] +pub enum JsTransportEnum { + Tcp, + Stdio, + Sse, +} + +/// Internal enum representing the underlying transport implementation. +/// +/// Not exposed directly to TypeScript/JavaScript users. +pub enum JsTransportInner { + Tcp(Option>>), + Stdio(Option<(Stdin, Stdout)>), + Sse(Option>), +} + +/// Transport handle for connecting to an RMCP server from TypeScript/JavaScript. +/// +/// Use the factory methods to create a transport for TCP, stdio, or SSE. +/// +/// # Example (TypeScript) +/// ```typescript +/// const tcpTransport = JsTransport.fromTcp(); +/// const sseTransport = JsTransport.fromSse(sseInstance, env); +/// ``` +#[napi] +pub struct JsTransport { + /// The type of transport (TCP, Stdio, or SSE). + pub kind: JsTransportEnum, + #[napi(skip)] + pub inner: Option, +} + +impl FromNapiValue for JsTransportInner { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + unsafe { + let obj = Object::from_napi_value(env, napi_val)?; + let kind: Option = obj.get("kind")?; + match kind.as_deref() { + Some("Tcp") => Ok(JsTransportInner::Tcp(None)), + Some("Stdio") => Ok(JsTransportInner::Stdio(None)), + Some("Sse") => Ok(JsTransportInner::Sse(None)), + _ => Err(napi::Error::from_reason("Invalid transport kind")), + } + } + } +} + +impl FromNapiValue for JsTransport { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + unsafe { + let obj = Object::from_napi_value(env, napi_val)?; + println!("FromNapiValue starting"); + let keys: Vec = Object::keys(&obj)?; + println!("FromNapiValue got keys: {:?}", keys); + println!("FromNapiValue about to get kind"); + + // Get kind as string since we're using string_enum + let kind_str: Option = obj.get("kind")?; + println!("FromNapiValue got kind as string: {:?}", kind_str); + let kind = match kind_str.as_deref() { + Some("Tcp") => JsTransportEnum::Tcp, + Some("Stdio") => JsTransportEnum::Stdio, + Some("Sse") => JsTransportEnum::Sse, + _ => return Err(napi::Error::from_reason("Invalid transport kind string")), + }; + + println!("FromNapiValue matched kind: {:?}", kind); + + // For SSE transport, we need to preserve the inner field + if kind == JsTransportEnum::Sse { + println!("FromNapiValue handling SSE transport"); + println!("FromNapiValue checking for inner property"); + let sse_prop = obj.get::<&str, Reference>("inner"); + println!("FromNapiValue inner property exists: {}", sse_prop.is_ok()); + if let Some(sse) = sse_prop? { + println!("FromNapiValue got SSE reference"); + let mut sse = sse.clone(env.into())?; + let inner = sse.inner.take().ok_or_else(|| napi::Error::from_reason("SSE transport not initialized"))?; + println!("FromNapiValue created SSE transport with inner"); + return Ok(JsTransport { + kind, + inner: Some(JsTransportInner::Sse(Some(inner))), + }); + } else { + println!("FromNapiValue no SSE reference found"); + return Err(napi::Error::from_reason("SSE transport reference not found")); + } + } + + // For other transport types, use the default inner + let inner = match kind { + JsTransportEnum::Tcp => Some(JsTransportInner::Tcp(None)), + JsTransportEnum::Stdio => Some(JsTransportInner::Stdio(None)), + JsTransportEnum::Sse => Some(JsTransportInner::Sse(None)), + }; + println!("FromNapiValue created transport with inner is_some: {}", inner.is_some()); + + Ok(JsTransport { kind, inner }) + } + } +} + +#[napi] +impl JsTransport { + /// Create a TCP transport for connecting to an RMCP server. + /// + /// # Example (TypeScript) + /// ```typescript + /// const transport = JsTransport.fromTcp(); + /// ``` + #[napi(factory)] + pub fn from_tcp() -> Self { + JsTransport { + kind: JsTransportEnum::Tcp, + inner: Some(JsTransportInner::Tcp(None)), + } + } + + /// Create a stdio transport for connecting to an RMCP server. + /// + /// # Example (TypeScript) + /// ```typescript + /// const transport = JsTransport.fromStdio(); + /// ``` + #[napi(factory)] + pub fn from_stdio() -> Self { + JsTransport { + kind: JsTransportEnum::Stdio, + inner: Some(JsTransportInner::Stdio(None)), + } + } + + /// Create an SSE transport for connecting to an RMCP server. + /// + /// # Arguments + /// * `sse` - An instance of `JsSseTransport` (as returned by `await JsSseTransport.start(endpoint)` in TypeScript). + /// + /// > **Note:** The `env` parameter is injected automatically by the napi binding and should NOT be provided by the user in TypeScript. + /// + /// # Example (TypeScript) + /// ```typescript + /// const sseEndpoint = 'http://localhost:8000/sse'; + /// const sseTransport = await JsSseTransport.start(sseEndpoint); + /// const transport = JsTransport.fromSse(sseTransport); + /// ``` + #[napi(factory)] + pub fn from_sse(sse: Reference, env: Env) -> napi::Result { + println!("JsTransport.fromSse received sse transport"); + let mut sse = sse.clone(env)?; + let inner = sse.inner.take().ok_or_else(|| napi::Error::from_reason("SSE transport not initialized"))?; + println!("JsTransport.fromSse successful"); + Ok(JsTransport { + kind: JsTransportEnum::Sse, + inner: Some(JsTransportInner::Sse(Some(inner))), + }) + } + + /// Get the kind of this transport (TCP, Stdio, or SSE). + #[napi(getter)] + pub fn kind(&self) -> JsTransportEnum { + self.kind.clone() + } + + /// Returns true if this transport is TCP. + #[napi] + pub fn is_tcp(&self) -> bool { + matches!(self.kind, JsTransportEnum::Tcp) + } + /// Returns true if this transport is stdio. + #[napi] + pub fn is_stdio(&self) -> bool { + matches!(self.kind, JsTransportEnum::Stdio) + } + /// Returns true if this transport is SSE. + #[napi] + pub fn is_sse(&self) -> bool { + matches!(self.kind, JsTransportEnum::Sse) + } +} diff --git a/bindings/typescript/src/transport/sse.rs b/bindings/typescript/src/transport/sse.rs new file mode 100644 index 00000000..fcbf6dc8 --- /dev/null +++ b/bindings/typescript/src/transport/sse.rs @@ -0,0 +1,30 @@ +use napi_derive::napi; +use rmcp::transport::SseTransport; +use rmcp::transport::sse::ReqwestSseClient; +use reqwest; + +#[napi] +pub struct JsSseTransport { + #[napi(skip)] + pub inner: Option>, +} + +#[napi] +impl JsSseTransport { + /// Async static constructor, just like PySseTransport::start + #[napi(factory)] + pub async fn start(url: String) -> napi::Result { + println!("JsSseTransport.start received URL: {}", url); + match SseTransport::start(&url).await { + Ok(transport) => { + println!("JsSseTransport.start successful"); + Ok(JsSseTransport { inner: Some(transport) }) + }, + Err(e) => { + println!("JsSseTransport.start error: {}", e); + Err(napi::Error::from_reason(e.to_string())) + }, + } + } + // Add more methods for interacting with the transport as needed +} diff --git a/bindings/typescript/tests/add.test.ts b/bindings/typescript/tests/add.test.ts new file mode 100644 index 00000000..04875093 --- /dev/null +++ b/bindings/typescript/tests/add.test.ts @@ -0,0 +1,16 @@ +// TypeScript test for rmcp_typescript wasm binding +import * as wasm from '../dist/rmcp_typescript/index.js'; + +// Test ProtocolVersion class +const pv = new wasm.ProtocolVersion("2.0"); +if (pv.value() !== "2.0") throw new Error('ProtocolVersion.value() failed'); +if (pv.toString() !== "2.0") throw new Error('ProtocolVersion.toString() failed'); + +// Test ProtocolVersion.latest static method +const latest = wasm.ProtocolVersion.latest(); +if (latest.value() !== "2.0") throw new Error('ProtocolVersion.latest() failed'); + +// Test json_rpc_version_2_0 function +if (wasm.json_rpc_version_2_0() !== "2.0") throw new Error('json_rpc_version_2_0() failed'); + +console.log('All ProtocolVersion and constant tests passed!'); diff --git a/bindings/typescript/tsconfig.json b/bindings/typescript/tsconfig.json new file mode 100644 index 00000000..cdeb6bd1 --- /dev/null +++ b/bindings/typescript/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "dist", + "types": ["./index.d.ts"] + }, + "include": [ + "rmcp_typescript/**/*.ts", + "examples/**/*.ts", + "tests/**/*.ts" + ] +}