ServicesExamples
Service and Tool Composition
Use a realistic composition scenario to show the common pattern where a service owns state and a tool exposes the model-facing call surface
Service and Tool Composition
Scenario
You have a SearchIndexService that owns:
- built search index state
- cached recent search results
But you also want the model to trigger "search project documents" through a tool.
That leads to a very common shape:
- the service owns long-lived state
- the tool is a thin facade
Example shape
class SearchIndexService extends BaseService {
readonly name = "search_index";
private readonly documents = new Map<string, string>();
indexDocument(id: string, text: string) {
this.documents.set(id, text);
}
search(query: string) {
const clean = String(query || "").trim().toLowerCase();
return [...this.documents.entries()]
.filter(([, text]) => text.toLowerCase().includes(clean))
.map(([id, text]) => ({ id, text }));
}
readonly actions = {};
}Then the tool becomes:
const searchTool = {
description: "Search indexed project documents",
inputSchema: {},
execute: async ({ query }: { query: string }) => {
return searchIndexService.search(query);
},
};Why this split is so common
Because the two layers solve different problems:
- the service answers "who owns the state?"
- the tool answers "how does the model call into it?"
When you do not need this split
If the capability:
- has no long-lived state
- is just one one-off query
then a pure tool is usually enough.