@isdk/tool-rpc
v1.1.0
Published
Client-server RPC and RESTful communication tools based on @isdk/tool-func.
Maintainers
Readme
@isdk/tool-rpc
@isdk/tool-rpc is a powerful TypeScript library that extends the modular capabilities of @isdk/tool-func to the network. It allows you to easily expose ToolFunc instances as a remote service and call them seamlessly from a client, just like local functions. It's perfect for building distributed AI agents, microservice architectures, or any scenario that requires tool invocation across different processes or machines.
This project is built on @isdk/tool-func. Before proceeding, please ensure you are familiar with its core concepts.
✨ Core Features
- Trinity Architecture (V2.6+):
- RpcTransportManager: Centralized registry for protocols, logical routing audit, and transport instance lifecycle.
- RpcServerDispatcher: Decoupled execution engine with built-in concurrency isolation (Shadow Instances) and task tracking.
- RpcActiveTaskTracker: Cross-protocol task visibility with retention policies and background execution support (102 Processing).
- Layered Abstractions: Provides a set of layered tool classes (
ServerTools,RpcMethodsServerTool,ResServerTools) that allow you to choose the right level of abstraction for your needs. - 🌐 RESTful Interface: Quickly create REST-style APIs using
ResServerTools, which automatically handles standard operations likeget,list,post,put, anddelete. - 🔧 RPC Method Grouping: Use
RpcMethodsServerToolto bundle multiple related functions (methods) under a single tool, invoked via anactparameter. - 🔌 Automatic Client-Side Proxies: The client (
ClientToolsand its subclasses) can automatically load tool definitions from the server and dynamically generate type-safe proxy functions, making remote calls as simple as local ones. - 🌐 Browser Compatible (
@isdk/tool-rpc/browser): A dedicated browser entry that includes client modules, abstract server-side classes (ServerTools,RpcMethodsServerTool,ResServerTools), and full transport infrastructure (RpcServerDispatcher,RpcTransportManager,RpcActiveTaskTracker, etc.). Only excludes Node.js-specific server transports (HttpServerToolTransport,MailboxServerTransport). - 🚀 Built-in HTTP & Mailbox Transport:
- HTTP: Comes with
HttpServerToolTransport(Node.jshttp) andHttpClientToolTransport(fetch, browser-compatible). - Mailbox: Native support for internal distributed mailbox protocols, ideal for cross-process or cross-thread communication.
- HTTP: Comes with
- 🚀 Multi-Instance Isolation: Support for multiple transport instances with different
apiUrls in a single process without global state collision. - 🛡️ Execution Guarding: Integrated deadline management (Soft/Hard deadlines) and physical-to-logical abort linkage.
- 🌊 Streaming Support: Both server and client support
ReadableStreamwith full lifecycle management, including backpressure and automatic cleanup.
📦 Installation
npm install @isdk/tool-rpc @isdk/tool-func🚀 Quick Start
The following example demonstrates how to create a RESTful tool, expose it on a server, and call it from a client.
Step 1: Define the Server-Side Tool
Create a class that extends ResServerTools. Implement standard methods like get and list, as well as any custom business logic in methods prefixed with $.
// ./tools/UserTool.ts
import { ResServerTools, ResServerFuncParams } from '@isdk/tool-rpc';
import { NotFoundError } from '@isdk/common-error';
// In-memory database for demonstration
const users: Record<string, any> = {
'1': { id: '1', name: 'Alice' },
'2': { id: '2', name: 'Bob' },
};
export class UserTool extends ResServerTools {
// Standard RESTful method: GET /api/users/:id
get({ id }: ResServerFuncParams) {
const user = users[id as string];
if (!user) throw new NotFoundError(id as string, 'user');
return user;
}
// Standard RESTful method: GET /api/users
list() {
return Object.values(users);
}
// Custom RPC-style method
$promote({ id }: ResServerFuncParams) {
const user = users[id as string];
if (!user) throw new NotFoundError(id as string, 'user');
return { ...user, role: 'admin' };
}
}Step 2: Set Up and Start the Server
In V2, we recommend using RpcTransportManager for lifecycle management and explicitly configuring the RpcServerDispatcher.
// ./server.ts
import { HttpServerToolTransport, RpcTransportManager, RpcServerDispatcher, ServerTools } from '@isdk/tool-rpc';
import { UserTool } from './tools/UserTool';
async function startServer() {
// 1. Register your tool instances in the global registry
new UserTool('users').register();
// 2. Initialize the transport with a dedicated Dispatcher
const apiUrl = 'http://localhost:3000/api';
const serverTransport = new HttpServerToolTransport({
apiUrl,
dispatcher: new RpcServerDispatcher({ registry: ServerTools.items })
});
// 3. Configure the logical routes for this transport
// Expose the tool definitions for client-side discovery
serverTransport.addDiscoveryHandler(apiUrl, () => ServerTools.toJSON());
// Expose the RPC endpoint for tool execution
serverTransport.addRpcHandler(apiUrl);
// 4. Register the transport to the Manager and start all services
RpcTransportManager.instance.addServer(serverTransport);
await RpcTransportManager.instance.startAll();
console.log(`✅ Tool server started at ${apiUrl}`);
}
startServer();Step 3: Set Up and Use the Client
In V2, the client (ResClientTools.loadFrom) uses RpcTransportManager to automatically resolve and manage transport instances based on the apiUrl.
// ./client.ts
import { HttpClientToolTransport, RpcTransportManager, ResClientTools } from '@isdk/tool-rpc';
// Define a type for full type-safety, including the custom method
type UserClientTool = ResClientTools & {
promote(params: { id: string }): Promise<{ id: string; name: string; role: string }>;
};
async function main() {
const apiUrl = 'http://localhost:3000/api';
// 1. Register the HTTP scheme (needed if not globally pre-registered)
RpcTransportManager.bindScheme('http', HttpClientToolTransport);
// 2. Load API definitions from the server.
// This automatically creates/uses a transport instance for the given apiUrl.
await ResClientTools.loadFrom(undefined, { apiUrl });
// 3. Get the dynamically created proxy for the remote tool.
const userTool = ResClientTools.get('users') as UserClientTool;
if (!userTool) {
throw new Error('Remote tool "users" not found!');
}
// 4. Call the remote APIs as if they were local methods!
// Calls GET /api/users/1
const user = await userTool.get!({ id: '1' });
console.log('Fetched User:', user); // { id: '1', name: 'Alice' }
// Calls GET /api/users
const allUsers = await userTool.list!();
console.log('All Users:', allUsers); // [{...}, {...}]
// Calls the custom RPC method $promote
const admin = await userTool.promote({ id: '2' });
console.log('Promoted User:', admin); // { id: '2', name: 'Bob', role: 'admin' }
}
main();Core Concepts: Layered Abstractions
The core design of @isdk/tool-rpc is a layered architecture that cleanly separates network communication from business logic. You can choose the right level of abstraction based on the complexity of your needs.
🏛️ Deep Architecture: Containers, Business, and Isolation
The architecture of @isdk/tool-rpc is built on a core philosophy: Static classes as Containers, Instances as Business Functions. This design balances global tool management with concrete business logic implementation.
1. The Duality Model
- Container Classes (Plural 'Tools'): e.g.,
ServerTools,RpcMethodsServerTool,ResServerTools.- Role: They act as static Registries and Discovery Protocol Filters.
- Responsibility: Define level specifications, manage tool collections, and implement the
toJSON()discovery algorithm. At the code level, they maintain global state via static properties (likeitems).
- Business Instances (Singular 'Tool'/'Func'): Instances created via
new ServerTools(...)or its subclasses.- Role: They are the concrete Functional Units.
- Responsibility: Host business logic (via the
funcmethod or$-prefixed RPC methods), and define parameter schemas (params) and metadata.
Naming Convention: Class names use the plural (Tools) to reflect their container nature, while instances or derived business logic are conceptually singular (Tool/Func).
2. Discovery Isolation & Layered Compatibility
To ensure clients load accurate, protocol-matching tool definitions during the Discovery phase, the project implements a hierarchical isolation mechanism.
Isolation Algorithm: The
toJSON()method performs triple validation during registry traversal:- Type Check:
item instanceof thisensures the tool belongs to the current or a sub-level. - Protocol Reference Check: Validates
item.constructor.toJSON === this.toJSON. This ensures that if a subclass (likeRpcMethodsServerTool) overridestoJSONfor a more complex protocol, its instances will not be exported by the parent class (likeServerTools), preventing basic clients from loading tools they cannot handle. - Visibility Check:
item.isApi !== false.
- Type Check:
Export Ranges by Level:
ServerTools.toJSON(): Exports only direct instances ofServerToolsand rawToolFuncitems marked asisApi: true. It never exports RPC or Resource tools.RpcMethodsServerTool.toJSON(): Exports all RPC-style tools. Due to downward compatibility, it includes instances of subclasses likeResServerTools.ResServerTools.toJSON(): Implements upward isolation, exporting only Resource tools and excluding general RPC methods from its parent.
Default Behavior for
isApi:- For Derived Business Instances (Func): Since they are designed for remote exposure, they are auto-exported by default unless explicitly set to
isApi: false. - For Base
ToolFuncInstances: For security, they are hidden by default. You must explicitly setisApi: trueto make them discoverable viaServerTools.
- For Derived Business Instances (Func): Since they are designed for remote exposure, they are auto-exported by default unless explicitly set to
graph TD
subgraph Client-Side
A[Client Application] --> B{ResClientTools};
B --> C{RpcMethodsClientTool};
C --> D{ClientTools};
end
subgraph Transport Layer
D --> |Uses| E[IClientToolTransport];
E -- HTTP Request --> F[IServerToolTransport];
end
subgraph Server-Side
F -->|Calls| G{ServerTools};
H{ResServerTools} --> I{RpcMethodsServerTool};
I --> G;
end
A -- Calls method on instance --> B;
B -- Transparently calls --> E;
F -- Receives request and finds --> H;
H -- Executes business logic --> H;
H -- Returns result through --> F;
F -- Sends HTTP Response --> E;
E -- Returns result to --> B;
B -- Returns result to --> A;3. 🛡️ Concurrency Isolation (Shadow Instances)
V2 Architecture introduces Shadow Instances to solve this.ctx race conditions in asynchronous environments.
- When a request arrives, the
Dispatchercreates a new instance usingObject.create(tool). - Property Shadowing:
this.ctx = contextonly affects the current execution instance, ensuring physical isolation during high concurrency while maintaining access to original tool properties.
4. 🚀 The Trinity Architecture (V2.6+)
The core of @isdk/tool-rpc is built around three decoupled components:
- RpcTransportManager (The Registry & Lifecycle):
- Physical Mapping: Manages
apiUrltoIToolTransportinstances. - Routing Audit: Performs
ListenAddr -> Set<RoutePath>conflict detection to prevent route hijacking on shared physical ports.
- Physical Mapping: Manages
- RpcServerDispatcher (The Logic Engine):
- Normalization: Translates protocol-specific objects into
ToolRpcContext. - Deadlines: Implements Soft Deadlines (triggering
102 Processingfor background execution) and Hard Deadlines (Terminating task with408).
- Normalization: Translates protocol-specific objects into
- RpcActiveTaskTracker (The Task Ledger):
- Observability: Tracks all active and background tasks across protocols.
- Retention: Manages result retention policies (
Once,Permanent, TTL). - Abort Linkage: Bridges
AbortSignalfrom physical connections to the execution layer.
🛠️ Advanced Architecture: Hierarchical Registries and Polymorphism
For complex systems with plugin architectures or multi-tenant environments, @isdk/tool-rpc leverages the Hierarchical Registries of the underlying @isdk/tool-func to support sophisticated customization.
1. Registry Isolation & Inheritance
By calling Tools.isolateRegistry(), you branch the current registry into a new scope using prototype inheritance:
- Inheritance: Sub-registries can access all tools from the parent.
- Locality: New registrations are local to the current scope (and its descendants) and do not pollute the parent.
- Use Case: Assigning independent tool domains to different AI agent groups or isolating Server/Client registries in unit tests.
2. Shadowing & Polymorphism
You can register a tool with the same name as a parent in an isolated registry to create a Shadow:
- Behavior: Calls within the local scope use the new implementation; calls in the parent scope still use the original.
- Use Case: Allowing specific plugins to replace a standard atomic tool (e.g., changing a notification method from Email to Slack) without modifying core framework code.
3. Smart Binding Strategies
When a workflow tool depends on other tools via the depends property, the binding strategy determines how those dependencies are resolved:
'auto'(Default - Context-Aware): The most intuitive mode for business logic.- Logic: Switches to polymorphic late binding only if the caller is a descendant of the tool definer.
- Benefit: Achieves "Local Customization, Global Stability". It allows plugins to modify a middle step of a workflow without breaking the parent system's internal stability.
'early'(Early Binding - Contract Protection):- Logic: Always uses the original dependency locked at registration time.
- Benefit: Used for security-critical or core components (e.g., auth, crypto) to prevent them from being "hijacked" by downstream shadows.
'late'(Late Binding - Environment-First):- Logic: Forcefully resolves from the top-level registry of the current caller, ignoring lineage.
- Benefit: Allows complete runtime injection based on the execution environment.
4. Namespace Protection
To prevent accidental shadowing, use allowOverride: false during registration. The registry will recursively check the prototype chain and throw an error if the name is already taken, protecting core APIs from being overwritten.
⚠️ Common Pitfalls
Defining
func()inResServerToolssubclasses- Consequence:
ResServerToolsrelies onfuncfor REST/RPC dispatching. Overwriting it without callingsuper.funcbreaksget/list/$methodrouting. - Best Practice: Always implement business logic via
get(),list(), or$-prefixed methods in the Resource layer.
- Consequence:
Treating
isApi: falseas a Firewall- Consequence:
isApi: falseonly affects the "Discovery" phase (hiding it from the client). If an attacker guesses the tool name and crafts a request, the transport layer will still execute it. - Best Practice: Implement authentication in your business logic or use the
_reqcontext for authorization.
- Consequence:
Missing Isolation in Single-Process Tests
- Consequence: Errors like
[ToolName] already registered as ServerTools. - Best Practice: Use
ClientTools.isolateRegistry()in yourbeforeAllhooks.
- Consequence: Errors like
Confusing Static 'Tools' vs. Instance 'Func' Responsibilities
- Consequence: Attempting to modify
ServerTools.itemswithin business logic, leading to global pollution. - Best Practice: Static members are for management; business logic should be strictly confined to
thisorthis.ctxon the instance.
- Consequence: Attempting to modify
Layer 1: ServerTools / ClientTools - The Basic Remote Function
This is the most fundamental layer, representing a single, remotely callable function.
ServerTools:Concept: An individual, remotely executable function.
Use Case: This is the simplest approach when you just need to expose a few scattered functions that don't have a strong relationship with each other.
Advanced Usage: Inside the
func, you can access the raw HTTP request and response objects viaparams._reqandparams._resfor lower-level control.Example:
// server.ts new ServerTools({ name: 'ping', isApi: true, // Mark as discoverable func: () => 'pong', }).register();
ClientTools:- Concept: The client-side proxy for a
ServerToolsinstance on the server. - How it Works: After
client.init(), it creates aClientToolsinstance namedping. When you callToolFunc.run('ping'), it sends the request over the network to the server.
- Concept: The client-side proxy for a
Layer 2: RpcMethodsServerTool / RpcMethodsClientTool - The Object-Oriented Service
This layer organizes multiple related functions into a collection that resembles an "object" or "service".
RpcMethodsServerTool:Concept: A "service" object containing multiple callable methods. It acts as a dispatcher.
Use Case: Use this class to better organize your code when you have a group of cohesive operations (e.g., a
UserServicewithcreateUser,updateUser,getUser).How it Works: Methods defined in the class with a
$prefix (e.g.,$createUser) are automatically registered as RPC methods. The client specifies which method to call by passingact: '$createUser'in the request.Example:
// server.ts class UserService extends RpcMethodsServerTool { $createUser({ name }) { // ... logic to create a user return { id: 'user-1', name }; } $getUser({ id }) { // ... logic to get a user return { id, name: 'Test User' }; } } new UserService('userService').register();
RpcMethodsClientTool:Concept: The client-side proxy for the remote service object.
How it Works: On initialization, it detects the
$createUserand$getUsermethods on the server and dynamically creates correspondingcreateUser()andgetUser()methods on the client instance. This makes the invocation look like a local object method call, completely hiding the underlyingactparameter and network communication.Example:
// client.ts const userService = RpcMethodsClientTool.get('userService'); const newUser = await userService.createUser({ name: 'Alice' });
Layer 3: ResServerTools / ResClientTools - The RESTful Resource
This is the highest-level abstraction, providing a resource-centric, RESTful-style API on top of RPC.
ResServerTools:- Concept: Represents a RESTful resource with built-in mapping for standard HTTP verbs (GET, POST, PUT, DELETE).
- Use Case: Ideal for scenarios requiring standard CRUD (Create, Read, Update, Delete) operations, such as managing
usersorproductsresources. - How it Works: It extends
RpcMethodsServerTooland pre-defines special methods likeget,list,post,put,delete. The HTTP transport layer intelligently calls the appropriate method based on the request'smethod(GET/POST) and whether anidis present in the URL.GET /users/:id->get({ id })GET /users->list()POST /users->post({ val })
- Advanced Usage: Since it extends
RpcMethodsServerTool, you can still define custom$methods in aResServerToolssubclass, allowing you to combine RESTful patterns with specific RPC calls (as shown in the Quick Start example on this page).
ResClientTools:Concept: The client-side proxy for a remote RESTful resource.
How it Works: It provides a set of convenient methods like
.get(),.list(),.post(), etc., which automatically construct and send requests that conform to REST semantics.Example:
// client.ts const userRes = ResClientTools.get('users'); const user = await userRes.get({ id: '1' }); // Sends GET /api/users/1 const allUsers = await userRes.list(); // Sends GET /api/users
🌐 Browser Usage
@isdk/tool-rpc/browser is a dedicated browser entry point that ships client modules, server-side abstractions, and full transport infrastructure — except Node.js-specific server transports (http, stream, etc.) are excluded.
Installation
npm install @isdk/tool-rpcImport from the browser entry
import {
// Client modules
ClientTools,
HttpClientToolTransport,
RpcClientTransportManager,
// Server-side abstractions
ServerTools,
RpcMethodsServerTool,
ResServerTools,
// Transport infrastructure
RpcTransportManager,
RpcServerDispatcher,
RpcActiveTaskTracker,
RPC_HEADERS,
} from '@isdk/tool-rpc/browser'
// Build a complete in-memory RPC server & client in the browser
RpcTransportManager.bindScheme('http', HttpClientToolTransport)
// Register a tool
ServerTools.register({
name: 'greet',
func: ({ name }) => `Hello, ${name}!`,
})
// Create a dispatcher
const dispatcher = new RpcServerDispatcher({ registry: ServerTools.items })
// Call remotely
await ClientTools.loadFrom(undefined, { apiUrl: 'http://localhost:3000/api/' })
const greet = ClientTools.get('greet')
const result = await greet.run({ name: 'Browser' })Key differences from Node.js
| Aspect | Node.js (@isdk/tool-rpc) | Browser (@isdk/tool-rpc/browser) |
|--------|---------------------------|-------------------------------------|
| Manager | RpcTransportManager (full version with server lifecycle) | RpcTransportManager + RpcClientTransportManager |
| Server transports | HttpServerToolTransport, MailboxServerTransport | Not included ❌ |
| Server-side abstractions | ServerTools, RpcMethodsServerTool, ResServerTools | All included ✅ |
| Transport infrastructure | RpcServerDispatcher, RpcActiveTaskTracker, RpcDeadlineGuard | All included ✅ |
| uuid generation | Via @isdk/hash (cross-platform) | Via @isdk/hash (cross-platform) |
| Bundle size | Larger (includes Node.js server transports) | Smaller (omits Node-http/mailbox transports)
Bundler auto-resolution
When using bundlers (webpack 5+, Rollup, Vite), the browser field in package.json automatically resolves @isdk/tool-rpc to the browser entry. This means you can also write:
// Bundlers automatically resolve to the browser entry
import { ClientTools } from '@isdk/tool-rpc'🔌 The Transport Layer
The transport layer is a core pillar of @isdk/tool-rpc, acting as the communication bridge between server tools and client tools to enable true Remote Procedure Calls (RPC).
Design Philosophy
The core design philosophy of the transport layer is the separation of concerns. It completely decouples the business logic of your tools (what you define in a Tool) from the implementation details of network communication (like protocol, routing, and serialization). This keeps your tool code pure and portable, without needing to know whether it's communicating over HTTP, WebSockets, or any other protocol. You define what your tool does, and the transport layer handles the rest.
Core Abstractions
The architecture is built around a few key interfaces:
IToolTransport: The common base interface for all transports, defining basic operations likemount.IServerToolTransport: The interface that a server-side transport must implement. Its core responsibilities are:- Exposing a Discovery Endpoint: Create a route, typically
GET(e.g.,/api), which, when accessed by a client, returns the JSON definitions of all registered and available tools. This is achieved via theaddDiscoveryHandlermethod. - Handling RPC Calls: Create a generic RPC route (e.g.,
/api/:toolId) that receives requests, finds the corresponding tool bytoolId, executes it, and returns the result. This is handled by theaddRpcHandlermethod. - Managing the server lifecycle (
start,stop). - Physical Awareness (V2): Methods like
getListenAddr()andgetRoutes()allow for physical port reuse and logical route auditing. - Route Claims & Remount (V2):
addRpcHandler()/addDiscoveryHandler()claim their path prefix in theServerToolTransportbase class, so one transport can serve several prefixes, each with its own registry and discovery handler. Logical routes are reported in canonical form (leading/, no trailing slash); the trailing-slash form used for prefix matching is derived bytoMatchPrefix(). Re-declaring an already claimed prefix replaces its registry/handler and logs a warning; passoptions.remount = 'throw'to raise an error instead — backends with their own router overrideonDuplicateRoute()to surface a native duplicate-route error.
- Exposing a Discovery Endpoint: Create a route, typically
IClientToolTransport: The interface that a client-side transport must implement. Its core responsibilities are:- Loading API Definitions: Call the
loadApis()method, which accesses the server's discovery endpoint to get the definitions of all tools. - Executing Remote Calls: Implement the
fetch()method, which is responsible for serializing the client's tool call (function name and parameters), sending it to the server's RPC endpoint, and processing the response. - Background Lifecycle (V2): Support for Polling (102 Processing) and Logical Cancellation linked to
AbortSignal.
- Loading API Definitions: Call the
Built-in Transports
The library provides plug-and-play transport implementations:
HttpServerToolTransport: A server-side transport that uses Node.js's built-inhttpmodule. In V2, it supports Longest Prefix Match routing, allowing multiple logical instances to share the same physical port.HttpClientToolTransport: A client-side transport that uses the cross-platformfetchAPI. It supports automatic polling for background tasks.Mailbox Transports: Native support for internal distributed mailbox protocols, ideal for cross-process or cross-thread communication with native physical isolation.
Extending the Functionality: Creating Your Own Transport
@isdk/tool-rpc is designed to be fully extensible. You can create your own transport layer to support different protocols (like WebSockets, gRPC) or to integrate with existing web frameworks (like Express, Koa, or Fastify) by implementing the interfaces described above.
Idea for a Fastify Integration:
- Create a
FastifyServerTransportclass that implementsIServerToolTransport. - In the
mountmethod, instead of creating a newhttpserver, you would accept an existingFastifyInstanceas an option. - Use
fastify.get(apiPrefix, ...)to register the discovery route, with a handler that callsServerTools.toJSON(). - Use
fastify.all(apiPrefix + '/:toolId', ...)to register the RPC route. The handler would extract parameters fromrequest.bodyorrequest.query, call the appropriate tool, and usereplyto send the response. - The
startandstopmethods could delegate to the Fastify instance'slistenandclosemethods.
This pluggable architecture gives @isdk/tool-rpc immense flexibility, allowing it to easily adapt to various project requirements and tech stacks.
Advanced Usage
This section covers some of the more powerful features of @isdk/tool-rpc for building flexible and efficient applications.
Exporting Functions for Client-Side Execution
In some scenarios, you might want to offload computation from the server or allow the client to execute a function locally. The allowExportFunc option enables this by serializing the function's body and sending it to the client during the discovery phase. When set to true, the client-side ClientTools will automatically use this downloaded function instead of making a network request.
Server-Side Configuration:
// server.ts
new ServerTools({
name: 'local-uuid',
isApi: true,
allowExportFunc: true, // Allow this function to be downloaded
func: () => {
// This logic will be executed on the client
console.log('Generating UUID on the client...');
return Math.random().toString(36).substring(2, 15);
},
}).register();Client-Side Usage:
// client.ts
const uuidTool = ClientTools.get('local-uuid');
// This call executes the downloaded function body. No network request is made.
const uuid = await uuidTool.run();
console.log('Generated UUID:', uuid);Automatic RESTful Method Routing
When using ResServerTools, the framework intelligently routes incoming HTTP requests. It inspects the HTTP method and the presence of an id parameter to determine which function to call. This logic, handled by getMethodFromParams within the tool, provides the following mappings out-of-the-box:
GET /api/my-resource→list()GET /api/my-resource/123→get({ id: '123' })POST /api/my-resource→post({ val: ... })
This allows you to write clean, resource-oriented code without worrying about manual routing.
Hybrid RESTful and RPC Tools
Since ResServerTools extends RpcMethodsServerTool, you can combine both API styles in a single tool. You can implement standard RESTful methods (get, list) while also adding custom RPC-style methods (e.g., $archive, $publish) for specific business operations that don't fit the CRUD model.
Automatic Parameter Type Casting
The params schema you define on a tool is used for more than just documentation. The server automatically uses it to cast incoming parameters to their specified types. For example, if you define id: { type: 'number' }, any id received from a URL path (which is a string) will be converted to a Number before your function is called.
Client-Side Method Aliasing
For convenience, when you define a method with a $ prefix on the server (e.g., $promoteUser), the client-side proxy automatically creates a cleaner alias without the prefix. This allows you to call myTool.promoteUser(...) on the client, making the code more readable and idiomatic.
🤝 Contributing
We welcome contributions of all kinds! Please read the CONTRIBUTING.md file for guidelines on how to get started.
📄 License
This project is licensed under the MIT License. See the LICENSE-MIT file for more details.
