Skip to content

Commit 77fafe7

Browse files
committed
feat: Add TypeScript bindings with capabilities support
1 parent 213593c commit 77fafe7

File tree

21 files changed

+838
-0
lines changed

21 files changed

+838
-0
lines changed

bindings/typescript/.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# TypeScript/Node ignores
2+
node_modules/
3+
dist/
4+
build/
5+
*.tsbuildinfo
6+
.env
7+
.env.*
8+
coverage/
9+
10+
# napi-rs ignores
11+
*.node
12+
index.node
13+
*.d.ts
14+
artifacts/
15+
.napi/
16+
17+
# Rust ignores
18+
target/
19+
Cargo.lock
20+
21+
# IDE ignores
22+
.vscode/
23+
.idea/
24+
*.swp
25+
*.swo
26+
27+
# OS ignores
28+
.DS_Store
29+
Thumbs.db

bindings/typescript/.placeholder

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

bindings/typescript/Cargo.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
name = "rmcp_typescript"
3+
edition.workspace = true
4+
version.workspace = true
5+
authors.workspace = true
6+
license.workspace = true
7+
repository.workspace = true
8+
description.workspace = true
9+
keywords.workspace = true
10+
homepage.workspace = true
11+
categories.workspace = true
12+
readme.workspace = true
13+
14+
[dependencies]
15+
rmcp = { path = "../../crates/rmcp", features = ["transport-sse", "client"] }
16+
napi = { version = "2", features = ["serde-json", "tokio_rt"] }
17+
napi-derive = "2"
18+
serde = { version = "1.0", features = ["derive"] }
19+
serde_json = "1.0"
20+
reqwest = { version = "0.12", features = ["json"] }
21+
sse-stream = "0.1.3"
22+
url = "2.4"
23+
tokio = { version = "1.0", features = ["full"] }
24+
tokio-util = { version = "0.7", features = ["full"] }
25+
[lib]
26+
crate-type = ["cdylib"]
27+
28+
[build-dependencies]
29+
napi-build = "2"

bindings/typescript/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
napi_build::setup();
3+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// TypeScript SSE client example for rmcp_typescript
2+
// Closely mirrors the structure and flow of sse.rs from the core Rust SDK
3+
4+
import { JsTransport, JsTransportEnum, JsClientInfo, JsImplementation } from '../../index';
5+
6+
// TODO: Replace 'any' with proper types from WASM/SDK when available
7+
// type ClientCapabilities = any;
8+
// type CallToolRequestParam = { name: string; arguments?: object };
9+
10+
async function main() {
11+
// Step 1: Initialize logging (console-based for now)
12+
console.info('Starting TypeScript SSE client example');
13+
14+
// Step 2: Create transport
15+
const sseEndpoint = 'http://localhost:8000/sse';
16+
const transport = new JsTransport(JsTransportEnum.SSE, sseEndpoint);
17+
18+
// Step 3: Define client info (mirror Rust structure)
19+
const clientInfo: JsClientInfo = {
20+
protocolVersion: '2024-11-05', // TODO: Use ProtocolVersion.latest() if available
21+
capabilities: {}, // TODO: Use proper ClientCapabilities
22+
clientInfo: {
23+
name: 'typescript-sse-client',
24+
version: '0.0.1',
25+
},
26+
};
27+
28+
try {
29+
// Step 4: Connect and serve (stub for now)
30+
transport.start(
31+
(data: string) => {
32+
console.log('Received SSE message:', data);
33+
// TODO: Parse and handle protocol messages here (initialize, tool list, etc.)
34+
},
35+
(err: Event) => {
36+
console.error('SSE error:', err);
37+
}
38+
);
39+
40+
// TODO: Replace with real async/await protocol flow when WASM/SDK methods are available
41+
// Example (pseudo-code):
42+
/*
43+
const client = await clientInfo.serve(transport);
44+
const serverInfo = client.peerInfo();
45+
console.info('Connected to server:', serverInfo);
46+
47+
const tools = await client.listTools({});
48+
console.info('Available tools:', tools);
49+
50+
const toolResult = await client.callTool({
51+
name: 'increment',
52+
arguments: {},
53+
});
54+
console.info('Tool result:', toolResult);
55+
56+
await client.cancel();
57+
*/
58+
59+
// For now, keep connection open for demonstration
60+
setTimeout(() => {
61+
transport.close();
62+
console.info('Connection closed.');
63+
}, 10000);
64+
65+
} catch (e) {
66+
console.error('Client error:', e);
67+
}
68+
}
69+
70+
main();

bindings/typescript/package-lock.json

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings/typescript/package.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "rmcp-typescript",
3+
"version": "0.1.0",
4+
"description": "TypeScript/NodeJS native binding for the rmcp Rust SDK (napi-rs)",
5+
"main": "./index.js",
6+
"types": "./index.d.ts",
7+
"scripts": {
8+
"build": "napi build --release",
9+
"test": "tsc && node tests/add.test.js"
10+
},
11+
"author": "",
12+
"license": "MIT",
13+
"devDependencies": {
14+
"typescript": "^5.8.3"
15+
}
16+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* ClientInfo represents information about the client, such as protocol version, capabilities, and implementation details.
3+
* This structure should mirror the Rust/Python SDK structure and will eventually be backed by WASM bindings.
4+
*/
5+
export interface ClientInfo {
6+
protocolVersion: string;
7+
capabilities: object; // Should be typed to ClientCapabilities
8+
clientInfo: Implementation;
9+
}
10+
11+
/**
12+
* Implementation details for the client, such as name and version.
13+
* This structure should mirror the Rust/Python SDK Implementation struct.
14+
*/
15+
export interface Implementation {
16+
name: string;
17+
version: string;
18+
}
19+
20+
/**
21+
* SSETransport provides an interface for connecting to an SSE (Server-Sent Events) endpoint.
22+
* In the browser, this would use EventSource; in Node.js, a compatible polyfill or HTTP client.
23+
* In the future, this should be backed by Rust/WASM for protocol logic.
24+
*/
25+
export class SSETransport {
26+
private url: string;
27+
private eventSource: EventSource | null = null;
28+
29+
/**
30+
* Create a new SSETransport for the given URL.
31+
* @param url The SSE endpoint URL.
32+
*/
33+
constructor(url: string) {
34+
this.url = url;
35+
}
36+
37+
/**
38+
* Start the SSE connection. In browser, uses EventSource. In Node, requires a polyfill.
39+
* @param onMessage Callback for each message event.
40+
* @param onError Callback for error events.
41+
*/
42+
start(onMessage: (data: any) => void, onError?: (err: any) => void) {
43+
if (typeof window !== 'undefined' && typeof window.EventSource !== 'undefined') {
44+
this.eventSource = new window.EventSource(this.url);
45+
this.eventSource.onmessage = (event) => {
46+
onMessage(event.data);
47+
};
48+
if (onError) {
49+
this.eventSource.onerror = onError;
50+
}
51+
} else {
52+
// Node.js: User must provide a compatible EventSource polyfill
53+
throw new Error('SSETransport requires EventSource (browser) or a polyfill (Node.js)');
54+
}
55+
}
56+
57+
/**
58+
* Close the SSE connection.
59+
*/
60+
close() {
61+
if (this.eventSource) {
62+
this.eventSource.close();
63+
this.eventSource = null;
64+
}
65+
}
66+
}
67+
68+
/**
69+
* IntoTransport is an interface for types that can be converted into a transport.
70+
* This is a placeholder for extensibility and should mirror the Rust trait.
71+
*/
72+
export interface IntoTransport {
73+
intoTransport(): SSETransport;
74+
}
75+
76+
// TODO: When Rust/WASM bindings are available, replace these stubs with real implementations and types.

bindings/typescript/src/client.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Client interface for rmcp_typescript binding
2+
// Add #[wasm_bindgen] wrappers as needed for TS/JS

bindings/typescript/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pub mod model;
2+
pub mod client;
3+
pub mod service;
4+
pub mod transport;
5+
6+
// Re-export capability types for WASM/TS bindings
7+
pub use model::capabilities::{
8+
JsPromptsCapability,
9+
JsResourcesCapability,
10+
JsToolsCapability,
11+
JsRootsCapabilities,
12+
JsExperimentalCapabilities,
13+
JsClientCapabilities,
14+
};
15+
16+
// Re-export model types
17+
pub use model::{
18+
JsClientInfo,
19+
JsImplementation,
20+
ProtocolVersion,
21+
};
22+
23+
// Re-export service types
24+
pub use service::JsPeer;
25+

0 commit comments

Comments
 (0)