@mcp-z/client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +159 -0
- package/LICENSE +21 -0
- package/README.md +90 -0
- package/dist/cjs/auth/capability-discovery.d.cts +25 -0
- package/dist/cjs/auth/capability-discovery.d.ts +25 -0
- package/dist/cjs/auth/capability-discovery.js +280 -0
- package/dist/cjs/auth/capability-discovery.js.map +1 -0
- package/dist/cjs/auth/index.d.cts +9 -0
- package/dist/cjs/auth/index.d.ts +9 -0
- package/dist/cjs/auth/index.js +28 -0
- package/dist/cjs/auth/index.js.map +1 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.cts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/cjs/auth/interactive-oauth-flow.js +537 -0
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/cjs/auth/oauth-callback-listener.d.cts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/cjs/auth/oauth-callback-listener.js +333 -0
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -0
- package/dist/cjs/auth/pkce.d.cts +17 -0
- package/dist/cjs/auth/pkce.d.ts +17 -0
- package/dist/cjs/auth/pkce.js +192 -0
- package/dist/cjs/auth/pkce.js.map +1 -0
- package/dist/cjs/auth/rfc9728-discovery.d.cts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/cjs/auth/rfc9728-discovery.js +436 -0
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -0
- package/dist/cjs/auth/types.d.cts +137 -0
- package/dist/cjs/auth/types.d.ts +137 -0
- package/dist/cjs/auth/types.js +9 -0
- package/dist/cjs/auth/types.js.map +1 -0
- package/dist/cjs/client-helpers.d.cts +55 -0
- package/dist/cjs/client-helpers.d.ts +55 -0
- package/dist/cjs/client-helpers.js +128 -0
- package/dist/cjs/client-helpers.js.map +1 -0
- package/dist/cjs/config/server-loader.d.cts +27 -0
- package/dist/cjs/config/server-loader.d.ts +27 -0
- package/dist/cjs/config/server-loader.js +111 -0
- package/dist/cjs/config/server-loader.js.map +1 -0
- package/dist/cjs/config/validate-config.d.cts +15 -0
- package/dist/cjs/config/validate-config.d.ts +15 -0
- package/dist/cjs/config/validate-config.js +128 -0
- package/dist/cjs/config/validate-config.js.map +1 -0
- package/dist/cjs/connection/connect-client.d.cts +59 -0
- package/dist/cjs/connection/connect-client.d.ts +59 -0
- package/dist/cjs/connection/connect-client.js +536 -0
- package/dist/cjs/connection/connect-client.js.map +1 -0
- package/dist/cjs/connection/existing-process-transport.d.cts +40 -0
- package/dist/cjs/connection/existing-process-transport.d.ts +40 -0
- package/dist/cjs/connection/existing-process-transport.js +274 -0
- package/dist/cjs/connection/existing-process-transport.js.map +1 -0
- package/dist/cjs/connection/types.d.cts +61 -0
- package/dist/cjs/connection/types.d.ts +61 -0
- package/dist/cjs/connection/types.js +53 -0
- package/dist/cjs/connection/types.js.map +1 -0
- package/dist/cjs/connection/wait-for-http-ready.d.cts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/cjs/connection/wait-for-http-ready.js +232 -0
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -0
- package/dist/cjs/dcr/dcr-authenticator.d.cts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/cjs/dcr/dcr-authenticator.js +655 -0
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.cts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js +245 -0
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/cjs/dcr/index.d.cts +8 -0
- package/dist/cjs/dcr/index.d.ts +8 -0
- package/dist/cjs/dcr/index.js +24 -0
- package/dist/cjs/dcr/index.js.map +1 -0
- package/dist/cjs/index.d.cts +21 -0
- package/dist/cjs/index.d.ts +21 -0
- package/dist/cjs/index.js +94 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/monkey-patches.d.cts +6 -0
- package/dist/cjs/monkey-patches.d.ts +6 -0
- package/dist/cjs/monkey-patches.js +236 -0
- package/dist/cjs/monkey-patches.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/response-wrappers.d.cts +41 -0
- package/dist/cjs/response-wrappers.d.ts +41 -0
- package/dist/cjs/response-wrappers.js +443 -0
- package/dist/cjs/response-wrappers.js.map +1 -0
- package/dist/cjs/search/index.d.cts +6 -0
- package/dist/cjs/search/index.d.ts +6 -0
- package/dist/cjs/search/index.js +25 -0
- package/dist/cjs/search/index.js.map +1 -0
- package/dist/cjs/search/search.d.cts +22 -0
- package/dist/cjs/search/search.d.ts +22 -0
- package/dist/cjs/search/search.js +630 -0
- package/dist/cjs/search/search.js.map +1 -0
- package/dist/cjs/search/types.d.cts +122 -0
- package/dist/cjs/search/types.d.ts +122 -0
- package/dist/cjs/search/types.js +10 -0
- package/dist/cjs/search/types.js.map +1 -0
- package/dist/cjs/spawn/spawn-server.d.cts +83 -0
- package/dist/cjs/spawn/spawn-server.d.ts +83 -0
- package/dist/cjs/spawn/spawn-server.js +410 -0
- package/dist/cjs/spawn/spawn-server.js.map +1 -0
- package/dist/cjs/spawn/spawn-servers.d.cts +151 -0
- package/dist/cjs/spawn/spawn-servers.d.ts +151 -0
- package/dist/cjs/spawn/spawn-servers.js +911 -0
- package/dist/cjs/spawn/spawn-servers.js.map +1 -0
- package/dist/cjs/types.d.cts +11 -0
- package/dist/cjs/types.d.ts +11 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/logger.d.cts +24 -0
- package/dist/cjs/utils/logger.d.ts +24 -0
- package/dist/cjs/utils/logger.js +80 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/cjs/utils/path-utils.d.cts +45 -0
- package/dist/cjs/utils/path-utils.d.ts +45 -0
- package/dist/cjs/utils/path-utils.js +158 -0
- package/dist/cjs/utils/path-utils.js.map +1 -0
- package/dist/cjs/utils/sanitizer.d.cts +30 -0
- package/dist/cjs/utils/sanitizer.d.ts +30 -0
- package/dist/cjs/utils/sanitizer.js +124 -0
- package/dist/cjs/utils/sanitizer.js.map +1 -0
- package/dist/esm/auth/capability-discovery.d.ts +25 -0
- package/dist/esm/auth/capability-discovery.js +110 -0
- package/dist/esm/auth/capability-discovery.js.map +1 -0
- package/dist/esm/auth/index.d.ts +9 -0
- package/dist/esm/auth/index.js +6 -0
- package/dist/esm/auth/index.js.map +1 -0
- package/dist/esm/auth/interactive-oauth-flow.d.ts +58 -0
- package/dist/esm/auth/interactive-oauth-flow.js +217 -0
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -0
- package/dist/esm/auth/oauth-callback-listener.d.ts +56 -0
- package/dist/esm/auth/oauth-callback-listener.js +166 -0
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -0
- package/dist/esm/auth/pkce.d.ts +17 -0
- package/dist/esm/auth/pkce.js +41 -0
- package/dist/esm/auth/pkce.js.map +1 -0
- package/dist/esm/auth/rfc9728-discovery.d.ts +34 -0
- package/dist/esm/auth/rfc9728-discovery.js +157 -0
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -0
- package/dist/esm/auth/types.d.ts +137 -0
- package/dist/esm/auth/types.js +7 -0
- package/dist/esm/auth/types.js.map +1 -0
- package/dist/esm/client-helpers.d.ts +55 -0
- package/dist/esm/client-helpers.js +81 -0
- package/dist/esm/client-helpers.js.map +1 -0
- package/dist/esm/config/server-loader.d.ts +27 -0
- package/dist/esm/config/server-loader.js +49 -0
- package/dist/esm/config/server-loader.js.map +1 -0
- package/dist/esm/config/validate-config.d.ts +15 -0
- package/dist/esm/config/validate-config.js +76 -0
- package/dist/esm/config/validate-config.js.map +1 -0
- package/dist/esm/connection/connect-client.d.ts +59 -0
- package/dist/esm/connection/connect-client.js +272 -0
- package/dist/esm/connection/connect-client.js.map +1 -0
- package/dist/esm/connection/existing-process-transport.d.ts +40 -0
- package/dist/esm/connection/existing-process-transport.js +103 -0
- package/dist/esm/connection/existing-process-transport.js.map +1 -0
- package/dist/esm/connection/types.d.ts +61 -0
- package/dist/esm/connection/types.js +34 -0
- package/dist/esm/connection/types.js.map +1 -0
- package/dist/esm/connection/wait-for-http-ready.d.ts +15 -0
- package/dist/esm/connection/wait-for-http-ready.js +43 -0
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -0
- package/dist/esm/dcr/dcr-authenticator.d.ts +73 -0
- package/dist/esm/dcr/dcr-authenticator.js +235 -0
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -0
- package/dist/esm/dcr/dynamic-client-registrar.d.ts +28 -0
- package/dist/esm/dcr/dynamic-client-registrar.js +66 -0
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -0
- package/dist/esm/dcr/index.d.ts +8 -0
- package/dist/esm/dcr/index.js +5 -0
- package/dist/esm/dcr/index.js.map +1 -0
- package/dist/esm/index.d.ts +21 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/monkey-patches.d.ts +6 -0
- package/dist/esm/monkey-patches.js +32 -0
- package/dist/esm/monkey-patches.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/response-wrappers.d.ts +41 -0
- package/dist/esm/response-wrappers.js +201 -0
- package/dist/esm/response-wrappers.js.map +1 -0
- package/dist/esm/search/index.d.ts +6 -0
- package/dist/esm/search/index.js +3 -0
- package/dist/esm/search/index.js.map +1 -0
- package/dist/esm/search/search.d.ts +22 -0
- package/dist/esm/search/search.js +236 -0
- package/dist/esm/search/search.js.map +1 -0
- package/dist/esm/search/types.d.ts +122 -0
- package/dist/esm/search/types.js +8 -0
- package/dist/esm/search/types.js.map +1 -0
- package/dist/esm/spawn/spawn-server.d.ts +83 -0
- package/dist/esm/spawn/spawn-server.js +145 -0
- package/dist/esm/spawn/spawn-server.js.map +1 -0
- package/dist/esm/spawn/spawn-servers.d.ts +151 -0
- package/dist/esm/spawn/spawn-servers.js +406 -0
- package/dist/esm/spawn/spawn-servers.js.map +1 -0
- package/dist/esm/types.d.ts +11 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/logger.d.ts +24 -0
- package/dist/esm/utils/logger.js +59 -0
- package/dist/esm/utils/logger.js.map +1 -0
- package/dist/esm/utils/path-utils.d.ts +45 -0
- package/dist/esm/utils/path-utils.js +89 -0
- package/dist/esm/utils/path-utils.js.map +1 -0
- package/dist/esm/utils/sanitizer.d.ts +30 -0
- package/dist/esm/utils/sanitizer.js +43 -0
- package/dist/esm/utils/sanitizer.js.map +1 -0
- package/package.json +92 -0
- package/schemas/servers.d.ts +90 -0
- package/schemas/servers.schema.json +104 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
export class ToolResponseError extends Error {
|
|
3
|
+
constructor(message, response){
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = 'ToolResponseError';
|
|
6
|
+
this.response = response;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export class ToolResponseWrapper {
|
|
10
|
+
raw() {
|
|
11
|
+
return this.payload;
|
|
12
|
+
}
|
|
13
|
+
json(validator) {
|
|
14
|
+
const value = this.resolveJsonPayload();
|
|
15
|
+
if (validator) {
|
|
16
|
+
validator(value);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
text() {
|
|
21
|
+
var _this_payload_content;
|
|
22
|
+
if (isCompatibilityResult(this.payload)) {
|
|
23
|
+
if (typeof this.payload.toolResult === 'string') {
|
|
24
|
+
return this.payload.toolResult;
|
|
25
|
+
}
|
|
26
|
+
throw new ToolResponseError('Compatibility tool result is not text', this.payload);
|
|
27
|
+
}
|
|
28
|
+
this.throwIfError();
|
|
29
|
+
const textBlock = findFirstTextBlock((_this_payload_content = this.payload.content) !== null && _this_payload_content !== void 0 ? _this_payload_content : []);
|
|
30
|
+
if (!textBlock) {
|
|
31
|
+
throw new ToolResponseError('Tool response did not include text content', this.payload);
|
|
32
|
+
}
|
|
33
|
+
return textBlock.text;
|
|
34
|
+
}
|
|
35
|
+
resolveJsonPayload() {
|
|
36
|
+
var _this_payload_content;
|
|
37
|
+
if (isCompatibilityResult(this.payload)) {
|
|
38
|
+
return this.payload.toolResult;
|
|
39
|
+
}
|
|
40
|
+
this.throwIfError();
|
|
41
|
+
if (hasStructuredContent(this.payload)) {
|
|
42
|
+
return this.payload.structuredContent;
|
|
43
|
+
}
|
|
44
|
+
const textBlock = findFirstTextBlock((_this_payload_content = this.payload.content) !== null && _this_payload_content !== void 0 ? _this_payload_content : []);
|
|
45
|
+
if (!textBlock) {
|
|
46
|
+
throw new ToolResponseError('Tool response did not include structuredContent or text content', this.payload);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(textBlock.text);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const reason = formatErrorReason(error);
|
|
52
|
+
throw new ToolResponseError(`Failed to parse tool text content as JSON: ${reason}`, this.payload);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throwIfError() {
|
|
56
|
+
if ('isError' in this.payload && this.payload.isError) {
|
|
57
|
+
var _this_payload_error_message;
|
|
58
|
+
let detail = typeof this.payload.error === 'object' && this.payload.error && 'message' in this.payload.error ? String((_this_payload_error_message = this.payload.error.message) !== null && _this_payload_error_message !== void 0 ? _this_payload_error_message : '') : '';
|
|
59
|
+
if (!detail) {
|
|
60
|
+
var _this_payload_content;
|
|
61
|
+
const textBlock = findFirstTextBlock((_this_payload_content = this.payload.content) !== null && _this_payload_content !== void 0 ? _this_payload_content : []);
|
|
62
|
+
if (textBlock === null || textBlock === void 0 ? void 0 : textBlock.text) {
|
|
63
|
+
detail = textBlock.text;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const message = detail ? `Tool invocation returned an error result: ${detail}` : 'Tool invocation returned an error result';
|
|
67
|
+
throw new ToolResponseError(message, this.payload);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
constructor(payload){
|
|
71
|
+
this.payload = payload;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export class PromptResponseError extends Error {
|
|
75
|
+
constructor(message, response){
|
|
76
|
+
super(message);
|
|
77
|
+
this.name = 'PromptResponseError';
|
|
78
|
+
this.response = response;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export class PromptResponseWrapper {
|
|
82
|
+
raw() {
|
|
83
|
+
return this.payload;
|
|
84
|
+
}
|
|
85
|
+
text() {
|
|
86
|
+
const segments = collectPromptText(this.payload.messages);
|
|
87
|
+
if (!segments.length) {
|
|
88
|
+
throw new PromptResponseError('Prompt response did not include text content', this.payload);
|
|
89
|
+
}
|
|
90
|
+
return segments.join('\n\n');
|
|
91
|
+
}
|
|
92
|
+
json(validator) {
|
|
93
|
+
const textValue = this.text();
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(textValue);
|
|
96
|
+
if (validator) {
|
|
97
|
+
validator(parsed);
|
|
98
|
+
}
|
|
99
|
+
return parsed;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
const reason = formatErrorReason(error);
|
|
102
|
+
throw new PromptResponseError(`Failed to parse prompt text as JSON: ${reason}`, this.payload);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
constructor(payload){
|
|
106
|
+
this.payload = payload;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export class ResourceResponseError extends Error {
|
|
110
|
+
constructor(message, response){
|
|
111
|
+
super(message);
|
|
112
|
+
this.name = 'ResourceResponseError';
|
|
113
|
+
this.response = response;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export class ResourceResponseWrapper {
|
|
117
|
+
raw() {
|
|
118
|
+
return this.payload;
|
|
119
|
+
}
|
|
120
|
+
text() {
|
|
121
|
+
const entry = this.firstEntry();
|
|
122
|
+
if ('text' in entry && typeof entry.text === 'string') {
|
|
123
|
+
return entry.text;
|
|
124
|
+
}
|
|
125
|
+
if ('blob' in entry && typeof entry.blob === 'string') {
|
|
126
|
+
try {
|
|
127
|
+
return Buffer.from(entry.blob, 'base64').toString('utf8');
|
|
128
|
+
} catch (error) {
|
|
129
|
+
const reason = formatErrorReason(error);
|
|
130
|
+
throw new ResourceResponseError(`Failed to decode resource blob as UTF-8 text: ${reason}`, this.payload);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
throw new ResourceResponseError('Resource content does not include text or blob data', this.payload);
|
|
134
|
+
}
|
|
135
|
+
json(validator) {
|
|
136
|
+
const textValue = this.text();
|
|
137
|
+
try {
|
|
138
|
+
const parsed = JSON.parse(textValue);
|
|
139
|
+
if (validator) {
|
|
140
|
+
validator(parsed);
|
|
141
|
+
}
|
|
142
|
+
return parsed;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
const reason = formatErrorReason(error);
|
|
145
|
+
throw new ResourceResponseError(`Failed to parse resource text as JSON: ${reason}`, this.payload);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
firstEntry() {
|
|
149
|
+
var _this_payload_contents;
|
|
150
|
+
const [entry] = (_this_payload_contents = this.payload.contents) !== null && _this_payload_contents !== void 0 ? _this_payload_contents : [];
|
|
151
|
+
if (!entry) {
|
|
152
|
+
throw new ResourceResponseError('Resource response did not include any contents', this.payload);
|
|
153
|
+
}
|
|
154
|
+
return entry;
|
|
155
|
+
}
|
|
156
|
+
constructor(payload){
|
|
157
|
+
this.payload = payload;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function hasStructuredContent(response) {
|
|
161
|
+
return Boolean(response.structuredContent);
|
|
162
|
+
}
|
|
163
|
+
function isCompatibilityResult(response) {
|
|
164
|
+
return hasOwn(response, 'toolResult');
|
|
165
|
+
}
|
|
166
|
+
function findFirstTextBlock(blocks) {
|
|
167
|
+
if (!Array.isArray(blocks)) {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
return blocks.find(isTextContent);
|
|
171
|
+
}
|
|
172
|
+
function collectPromptText(messages) {
|
|
173
|
+
const segments = [];
|
|
174
|
+
for (const message of messages){
|
|
175
|
+
const textBlock = isTextContent(message.content) ? message.content : undefined;
|
|
176
|
+
if (textBlock) {
|
|
177
|
+
segments.push(textBlock.text);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return segments;
|
|
181
|
+
}
|
|
182
|
+
function isTextContent(block) {
|
|
183
|
+
return Boolean(block) && typeof block === 'object' && block.type === 'text';
|
|
184
|
+
}
|
|
185
|
+
const protoHasOwn = Object.prototype.hasOwnProperty;
|
|
186
|
+
function hasOwn(target, key) {
|
|
187
|
+
return protoHasOwn.call(target, key);
|
|
188
|
+
}
|
|
189
|
+
function formatErrorReason(error) {
|
|
190
|
+
if (error instanceof Error && typeof error.message === 'string') {
|
|
191
|
+
return error.message;
|
|
192
|
+
}
|
|
193
|
+
if (typeof error === 'string') {
|
|
194
|
+
return error;
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return JSON.stringify(error);
|
|
198
|
+
} catch {
|
|
199
|
+
return String(error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/response-wrappers.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { ContentBlock, PromptMessage, TextContent } from '@modelcontextprotocol/sdk/types.js';\n\nexport type NativeCallToolResponse = Awaited<ReturnType<Client['callTool']>>;\nexport type NativeGetPromptResponse = Awaited<ReturnType<Client['getPrompt']>>;\nexport type NativeReadResourceResponse = Awaited<ReturnType<Client['readResource']>>;\n\nexport type JsonValidator<T> = (value: unknown) => asserts value is T;\n\nexport class ToolResponseError extends Error {\n readonly response: NativeCallToolResponse;\n\n constructor(message: string, response: NativeCallToolResponse) {\n super(message);\n this.name = 'ToolResponseError';\n this.response = response;\n }\n}\n\nexport class ToolResponseWrapper {\n private readonly payload: NativeCallToolResponse;\n\n constructor(payload: NativeCallToolResponse) {\n this.payload = payload;\n }\n\n raw(): NativeCallToolResponse {\n return this.payload;\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const value = this.resolveJsonPayload();\n if (validator) {\n validator(value);\n }\n return value as T;\n }\n\n text(): string {\n if (isCompatibilityResult(this.payload)) {\n if (typeof this.payload.toolResult === 'string') {\n return this.payload.toolResult;\n }\n throw new ToolResponseError('Compatibility tool result is not text', this.payload);\n }\n\n this.throwIfError();\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include text content', this.payload);\n }\n\n return textBlock.text;\n }\n\n private resolveJsonPayload(): unknown {\n if (isCompatibilityResult(this.payload)) {\n return this.payload.toolResult;\n }\n\n this.throwIfError();\n\n if (hasStructuredContent(this.payload)) {\n return this.payload.structuredContent;\n }\n\n const textBlock = findFirstTextBlock(this.payload.content ?? []);\n if (!textBlock) {\n throw new ToolResponseError('Tool response did not include structuredContent or text content', this.payload);\n }\n\n try {\n return JSON.parse(textBlock.text) as unknown;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ToolResponseError(`Failed to parse tool text content as JSON: ${reason}`, this.payload);\n }\n }\n\n private throwIfError(): void {\n if ('isError' in this.payload && this.payload.isError) {\n let detail = typeof this.payload.error === 'object' && this.payload.error && 'message' in this.payload.error ? String((this.payload.error as { message?: unknown }).message ?? '') : '';\n if (!detail) {\n const textBlock = findFirstTextBlock((this.payload.content ?? []) as ContentBlock[]);\n if (textBlock?.text) {\n detail = textBlock.text;\n }\n }\n const message = detail ? `Tool invocation returned an error result: ${detail}` : 'Tool invocation returned an error result';\n throw new ToolResponseError(message, this.payload);\n }\n }\n}\n\nexport class PromptResponseError extends Error {\n readonly response: NativeGetPromptResponse;\n\n constructor(message: string, response: NativeGetPromptResponse) {\n super(message);\n this.name = 'PromptResponseError';\n this.response = response;\n }\n}\n\nexport class PromptResponseWrapper {\n private readonly payload: NativeGetPromptResponse;\n\n constructor(payload: NativeGetPromptResponse) {\n this.payload = payload;\n }\n\n raw(): NativeGetPromptResponse {\n return this.payload;\n }\n\n text(): string {\n const segments = collectPromptText(this.payload.messages);\n if (!segments.length) {\n throw new PromptResponseError('Prompt response did not include text content', this.payload);\n }\n return segments.join('\\n\\n');\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n validator(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new PromptResponseError(`Failed to parse prompt text as JSON: ${reason}`, this.payload);\n }\n }\n}\n\nexport class ResourceResponseError extends Error {\n readonly response: NativeReadResourceResponse;\n\n constructor(message: string, response: NativeReadResourceResponse) {\n super(message);\n this.name = 'ResourceResponseError';\n this.response = response;\n }\n}\n\nexport class ResourceResponseWrapper {\n private readonly payload: NativeReadResourceResponse;\n\n constructor(payload: NativeReadResourceResponse) {\n this.payload = payload;\n }\n\n raw(): NativeReadResourceResponse {\n return this.payload;\n }\n\n text(): string {\n const entry = this.firstEntry();\n if ('text' in entry && typeof entry.text === 'string') {\n return entry.text;\n }\n if ('blob' in entry && typeof entry.blob === 'string') {\n try {\n return Buffer.from(entry.blob, 'base64').toString('utf8');\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to decode resource blob as UTF-8 text: ${reason}`, this.payload);\n }\n }\n throw new ResourceResponseError('Resource content does not include text or blob data', this.payload);\n }\n\n json<T = unknown>(validator?: JsonValidator<T>): T {\n const textValue = this.text();\n try {\n const parsed = JSON.parse(textValue) as unknown;\n if (validator) {\n validator(parsed);\n }\n return parsed as T;\n } catch (error) {\n const reason = formatErrorReason(error);\n throw new ResourceResponseError(`Failed to parse resource text as JSON: ${reason}`, this.payload);\n }\n }\n\n private firstEntry(): NativeReadResourceResponse['contents'][number] {\n const [entry] = this.payload.contents ?? [];\n if (!entry) {\n throw new ResourceResponseError('Resource response did not include any contents', this.payload);\n }\n return entry;\n }\n}\n\nfunction hasStructuredContent(response: NativeCallToolResponse): response is NativeCallToolResponse & { structuredContent: Record<string, unknown> } {\n return Boolean((response as { structuredContent?: unknown }).structuredContent);\n}\n\nfunction isCompatibilityResult(response: NativeCallToolResponse): response is NativeCallToolResponse & { toolResult: unknown } {\n return hasOwn(response, 'toolResult');\n}\n\nfunction findFirstTextBlock(blocks: ContentBlock[] | undefined): TextContent | undefined {\n if (!Array.isArray(blocks)) {\n return undefined;\n }\n return blocks.find(isTextContent);\n}\n\nfunction collectPromptText(messages: PromptMessage[]): string[] {\n const segments: string[] = [];\n for (const message of messages) {\n const textBlock = isTextContent(message.content) ? message.content : undefined;\n if (textBlock) {\n segments.push(textBlock.text);\n }\n }\n return segments;\n}\n\nfunction isTextContent(block: unknown): block is TextContent {\n return Boolean(block) && typeof block === 'object' && (block as { type?: string }).type === 'text';\n}\n\nconst protoHasOwn = Object.prototype.hasOwnProperty;\n\nfunction hasOwn(target: object, key: PropertyKey): boolean {\n return protoHasOwn.call(target, key);\n}\n\nfunction formatErrorReason(error: unknown): string {\n if (error instanceof Error && typeof error.message === 'string') {\n return error.message;\n }\n if (typeof error === 'string') {\n return error;\n }\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n"],"names":["Buffer","ToolResponseError","Error","message","response","name","ToolResponseWrapper","raw","payload","json","validator","value","resolveJsonPayload","text","isCompatibilityResult","toolResult","throwIfError","textBlock","findFirstTextBlock","content","hasStructuredContent","structuredContent","JSON","parse","error","reason","formatErrorReason","isError","detail","String","PromptResponseError","PromptResponseWrapper","segments","collectPromptText","messages","length","join","textValue","parsed","ResourceResponseError","ResourceResponseWrapper","entry","firstEntry","blob","from","toString","contents","Boolean","hasOwn","blocks","Array","isArray","undefined","find","isTextContent","push","block","type","protoHasOwn","Object","prototype","hasOwnProperty","target","key","call","stringify"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search module for MCP capability discovery
|
|
3
|
+
*/
|
|
4
|
+
export type { CapabilityClient } from './search.js';
|
|
5
|
+
export { buildCapabilityIndex, search, searchCapabilities } from './search.js';
|
|
6
|
+
export type { CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult, } from './types.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/search/index.ts"],"sourcesContent":["/**\n * Search module for MCP capability discovery\n */\n\nexport type { CapabilityClient } from './search.ts';\nexport { buildCapabilityIndex, search, searchCapabilities } from './search.ts';\nexport type {\n CapabilityIndex,\n CapabilityType,\n IndexedCapability,\n IndexedPrompt,\n IndexedResource,\n IndexedTool,\n SearchField,\n SearchOptions,\n SearchResponse,\n SearchResult,\n} from './types.ts';\n"],"names":["buildCapabilityIndex","search","searchCapabilities"],"mappings":"AAAA;;CAEC,GAGD,SAASA,oBAAoB,EAAEC,MAAM,EAAEC,kBAAkB,QAAQ,cAAc"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search implementation for MCP capability discovery
|
|
3
|
+
*
|
|
4
|
+
* Provides text-based search across tools, prompts, and resources
|
|
5
|
+
* from connected MCP servers.
|
|
6
|
+
*/
|
|
7
|
+
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
8
|
+
import type { CapabilityIndex, SearchOptions, SearchResponse } from './types.js';
|
|
9
|
+
export type CapabilityClient = Pick<Client, 'listTools' | 'listPrompts' | 'listResources'>;
|
|
10
|
+
/**
|
|
11
|
+
* Build an index of capabilities from connected MCP clients
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildCapabilityIndex(clients: Map<string, CapabilityClient>): Promise<CapabilityIndex>;
|
|
14
|
+
/**
|
|
15
|
+
* Search for capabilities matching a query string
|
|
16
|
+
*/
|
|
17
|
+
export declare function searchCapabilities(index: CapabilityIndex, query: string, options?: SearchOptions): SearchResponse;
|
|
18
|
+
/**
|
|
19
|
+
* Convenience function to search directly from connected clients
|
|
20
|
+
* Builds index and performs search in one call
|
|
21
|
+
*/
|
|
22
|
+
export declare function search(clients: Map<string, CapabilityClient>, query: string, options?: SearchOptions): Promise<SearchResponse>;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search implementation for MCP capability discovery
|
|
3
|
+
*
|
|
4
|
+
* Provides text-based search across tools, prompts, and resources
|
|
5
|
+
* from connected MCP servers.
|
|
6
|
+
*/ const DEFAULT_LIMIT = 20;
|
|
7
|
+
const DEFAULT_THRESHOLD = 0;
|
|
8
|
+
const DEFAULT_TYPES = [
|
|
9
|
+
'tool',
|
|
10
|
+
'prompt',
|
|
11
|
+
'resource'
|
|
12
|
+
];
|
|
13
|
+
const DEFAULT_SEARCH_FIELDS = [
|
|
14
|
+
'name',
|
|
15
|
+
'description',
|
|
16
|
+
'schema'
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Extract searchable text from a JSON Schema's property descriptions
|
|
20
|
+
*/ function extractSchemaText(inputSchema) {
|
|
21
|
+
if (!inputSchema || typeof inputSchema !== 'object') {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
const schema = inputSchema;
|
|
25
|
+
const parts = [];
|
|
26
|
+
// Add schema-level description if present
|
|
27
|
+
if (schema.description) {
|
|
28
|
+
parts.push(schema.description);
|
|
29
|
+
}
|
|
30
|
+
// Add property names and descriptions
|
|
31
|
+
if (schema.properties) {
|
|
32
|
+
for (const [propName, prop] of Object.entries(schema.properties)){
|
|
33
|
+
parts.push(propName);
|
|
34
|
+
if (prop && typeof prop === 'object' && prop.description) {
|
|
35
|
+
parts.push(prop.description);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return parts.join(' ');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Extract searchable text from prompt arguments
|
|
43
|
+
*/ function extractArgumentsText(args) {
|
|
44
|
+
if (!args || !Array.isArray(args)) {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
return args.map((arg)=>{
|
|
48
|
+
const parts = [
|
|
49
|
+
arg.name
|
|
50
|
+
];
|
|
51
|
+
if (arg.description) {
|
|
52
|
+
parts.push(arg.description);
|
|
53
|
+
}
|
|
54
|
+
return parts.join(' ');
|
|
55
|
+
}).join(' ');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build an index of capabilities from connected MCP clients
|
|
59
|
+
*/ export async function buildCapabilityIndex(clients) {
|
|
60
|
+
const capabilities = [];
|
|
61
|
+
const servers = [];
|
|
62
|
+
for (const [serverName, client] of clients){
|
|
63
|
+
servers.push(serverName);
|
|
64
|
+
// Fetch all capabilities in parallel, handling errors gracefully
|
|
65
|
+
const [toolsResult, promptsResult, resourcesResult] = await Promise.all([
|
|
66
|
+
client.listTools().catch(()=>null),
|
|
67
|
+
client.listPrompts().catch(()=>null),
|
|
68
|
+
client.listResources().catch(()=>null)
|
|
69
|
+
]);
|
|
70
|
+
// Index tools
|
|
71
|
+
if (toolsResult === null || toolsResult === void 0 ? void 0 : toolsResult.tools) {
|
|
72
|
+
for (const tool of toolsResult.tools){
|
|
73
|
+
capabilities.push({
|
|
74
|
+
type: 'tool',
|
|
75
|
+
server: serverName,
|
|
76
|
+
name: tool.name,
|
|
77
|
+
description: tool.description,
|
|
78
|
+
schemaText: extractSchemaText(tool.inputSchema)
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Index prompts
|
|
83
|
+
if (promptsResult === null || promptsResult === void 0 ? void 0 : promptsResult.prompts) {
|
|
84
|
+
for (const prompt of promptsResult.prompts){
|
|
85
|
+
capabilities.push({
|
|
86
|
+
type: 'prompt',
|
|
87
|
+
server: serverName,
|
|
88
|
+
name: prompt.name,
|
|
89
|
+
description: prompt.description,
|
|
90
|
+
argumentsText: extractArgumentsText(prompt.arguments),
|
|
91
|
+
arguments: prompt.arguments
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Index resources
|
|
96
|
+
if (resourcesResult === null || resourcesResult === void 0 ? void 0 : resourcesResult.resources) {
|
|
97
|
+
for (const resource of resourcesResult.resources){
|
|
98
|
+
capabilities.push({
|
|
99
|
+
type: 'resource',
|
|
100
|
+
server: serverName,
|
|
101
|
+
name: resource.name,
|
|
102
|
+
description: resource.description,
|
|
103
|
+
uri: resource.uri,
|
|
104
|
+
mimeType: resource.mimeType
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
capabilities,
|
|
111
|
+
servers,
|
|
112
|
+
indexedAt: new Date()
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Calculate relevance score and matched fields for a capability against a query
|
|
117
|
+
*/ function scoreCapability(capability, queryTerms, searchFields) {
|
|
118
|
+
const matchedOn = [];
|
|
119
|
+
let totalScore = 0;
|
|
120
|
+
// Weights for different match types
|
|
121
|
+
const EXACT_NAME_WEIGHT = 1.0;
|
|
122
|
+
const PARTIAL_NAME_WEIGHT = 0.8;
|
|
123
|
+
const DESCRIPTION_WEIGHT = 0.6;
|
|
124
|
+
const SCHEMA_WEIGHT = 0.4;
|
|
125
|
+
const SERVER_WEIGHT = 0.3;
|
|
126
|
+
const nameLower = capability.name.toLowerCase();
|
|
127
|
+
const descLower = (capability.description || '').toLowerCase();
|
|
128
|
+
const serverLower = capability.server.toLowerCase();
|
|
129
|
+
// Get schema/arguments text based on type
|
|
130
|
+
let schemaTextLower = '';
|
|
131
|
+
if (capability.type === 'tool') {
|
|
132
|
+
schemaTextLower = capability.schemaText.toLowerCase();
|
|
133
|
+
} else if (capability.type === 'prompt') {
|
|
134
|
+
schemaTextLower = capability.argumentsText.toLowerCase();
|
|
135
|
+
} else if (capability.type === 'resource') {
|
|
136
|
+
// For resources, include URI and mimeType in searchable text
|
|
137
|
+
schemaTextLower = `${capability.uri} ${capability.mimeType || ''}`.toLowerCase();
|
|
138
|
+
}
|
|
139
|
+
for (const term of queryTerms){
|
|
140
|
+
const termLower = term.toLowerCase();
|
|
141
|
+
// Check name matches
|
|
142
|
+
if (searchFields.includes('name')) {
|
|
143
|
+
if (nameLower === termLower) {
|
|
144
|
+
totalScore += EXACT_NAME_WEIGHT;
|
|
145
|
+
if (!matchedOn.includes('name')) matchedOn.push('name');
|
|
146
|
+
} else if (nameLower.includes(termLower)) {
|
|
147
|
+
totalScore += PARTIAL_NAME_WEIGHT;
|
|
148
|
+
if (!matchedOn.includes('name')) matchedOn.push('name');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Check description matches
|
|
152
|
+
if (searchFields.includes('description') && descLower.includes(termLower)) {
|
|
153
|
+
totalScore += DESCRIPTION_WEIGHT;
|
|
154
|
+
if (!matchedOn.includes('description')) matchedOn.push('description');
|
|
155
|
+
}
|
|
156
|
+
// Check schema/arguments matches
|
|
157
|
+
if (searchFields.includes('schema') && schemaTextLower.includes(termLower)) {
|
|
158
|
+
totalScore += SCHEMA_WEIGHT;
|
|
159
|
+
const fieldName = capability.type === 'tool' ? 'inputSchema' : capability.type === 'prompt' ? 'arguments' : 'uri';
|
|
160
|
+
if (!matchedOn.includes(fieldName)) matchedOn.push(fieldName);
|
|
161
|
+
}
|
|
162
|
+
// Check server name matches
|
|
163
|
+
if (searchFields.includes('server') && serverLower.includes(termLower)) {
|
|
164
|
+
totalScore += SERVER_WEIGHT;
|
|
165
|
+
if (!matchedOn.includes('server')) matchedOn.push('server');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Normalize score to 0-1 range based on number of terms
|
|
169
|
+
const normalizedScore = queryTerms.length > 0 ? Math.min(1, totalScore / queryTerms.length) : 0;
|
|
170
|
+
return {
|
|
171
|
+
score: normalizedScore,
|
|
172
|
+
matchedOn
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Search for capabilities matching a query string
|
|
177
|
+
*/ export function searchCapabilities(index, query, options = {}) {
|
|
178
|
+
const { types = DEFAULT_TYPES, servers, searchFields = DEFAULT_SEARCH_FIELDS, limit = DEFAULT_LIMIT, threshold = DEFAULT_THRESHOLD } = options;
|
|
179
|
+
// Tokenize query into search terms
|
|
180
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((term)=>term.length > 0);
|
|
181
|
+
// If empty query, return empty results
|
|
182
|
+
if (queryTerms.length === 0) {
|
|
183
|
+
return {
|
|
184
|
+
query,
|
|
185
|
+
results: [],
|
|
186
|
+
total: 0
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// Filter and score capabilities
|
|
190
|
+
const scoredResults = [];
|
|
191
|
+
for (const capability of index.capabilities){
|
|
192
|
+
// Filter by type
|
|
193
|
+
if (!types.includes(capability.type)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
// Filter by server
|
|
197
|
+
if (servers && servers.length > 0 && !servers.includes(capability.server)) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// Score the capability
|
|
201
|
+
const { score, matchedOn } = scoreCapability(capability, queryTerms, searchFields);
|
|
202
|
+
// Apply threshold filter
|
|
203
|
+
if (score >= threshold && matchedOn.length > 0) {
|
|
204
|
+
scoredResults.push({
|
|
205
|
+
capability,
|
|
206
|
+
score,
|
|
207
|
+
matchedOn
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Sort by score descending
|
|
212
|
+
scoredResults.sort((a, b)=>b.score - a.score);
|
|
213
|
+
// Get total before limiting
|
|
214
|
+
const total = scoredResults.length;
|
|
215
|
+
// Apply limit and transform to SearchResult
|
|
216
|
+
const results = scoredResults.slice(0, limit).map(({ capability, score, matchedOn })=>({
|
|
217
|
+
type: capability.type,
|
|
218
|
+
server: capability.server,
|
|
219
|
+
name: capability.name,
|
|
220
|
+
description: capability.description,
|
|
221
|
+
matchedOn,
|
|
222
|
+
score
|
|
223
|
+
}));
|
|
224
|
+
return {
|
|
225
|
+
query,
|
|
226
|
+
results,
|
|
227
|
+
total
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Convenience function to search directly from connected clients
|
|
232
|
+
* Builds index and performs search in one call
|
|
233
|
+
*/ export async function search(clients, query, options = {}) {
|
|
234
|
+
const index = await buildCapabilityIndex(clients);
|
|
235
|
+
return searchCapabilities(index, query, options);
|
|
236
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/search/search.ts"],"sourcesContent":["/**\n * Search implementation for MCP capability discovery\n *\n * Provides text-based search across tools, prompts, and resources\n * from connected MCP servers.\n */\n\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { PromptArgument } from '../connection/types.ts';\nimport type { CapabilityIndex, CapabilityType, IndexedCapability, IndexedPrompt, IndexedResource, IndexedTool, SearchField, SearchOptions, SearchResponse, SearchResult } from './types.ts';\n\nexport type CapabilityClient = Pick<Client, 'listTools' | 'listPrompts' | 'listResources'>;\n\nconst DEFAULT_LIMIT = 20;\nconst DEFAULT_THRESHOLD = 0;\nconst DEFAULT_TYPES: CapabilityType[] = ['tool', 'prompt', 'resource'];\nconst DEFAULT_SEARCH_FIELDS: SearchField[] = ['name', 'description', 'schema'];\n\n/**\n * Extract searchable text from a JSON Schema's property descriptions\n */\nfunction extractSchemaText(inputSchema: unknown): string {\n if (!inputSchema || typeof inputSchema !== 'object') {\n return '';\n }\n\n const schema = inputSchema as {\n properties?: Record<string, { description?: string; name?: string }>;\n description?: string;\n };\n\n const parts: string[] = [];\n\n // Add schema-level description if present\n if (schema.description) {\n parts.push(schema.description);\n }\n\n // Add property names and descriptions\n if (schema.properties) {\n for (const [propName, prop] of Object.entries(schema.properties)) {\n parts.push(propName);\n if (prop && typeof prop === 'object' && prop.description) {\n parts.push(prop.description);\n }\n }\n }\n\n return parts.join(' ');\n}\n\n/**\n * Extract searchable text from prompt arguments\n */\nfunction extractArgumentsText(args: PromptArgument[] | undefined): string {\n if (!args || !Array.isArray(args)) {\n return '';\n }\n\n return args\n .map((arg) => {\n const parts = [arg.name];\n if (arg.description) {\n parts.push(arg.description);\n }\n return parts.join(' ');\n })\n .join(' ');\n}\n\n/**\n * Build an index of capabilities from connected MCP clients\n */\nexport async function buildCapabilityIndex(clients: Map<string, CapabilityClient>): Promise<CapabilityIndex> {\n const capabilities: IndexedCapability[] = [];\n const servers: string[] = [];\n\n for (const [serverName, client] of clients) {\n servers.push(serverName);\n\n // Fetch all capabilities in parallel, handling errors gracefully\n const [toolsResult, promptsResult, resourcesResult] = await Promise.all([client.listTools().catch(() => null), client.listPrompts().catch(() => null), client.listResources().catch(() => null)]);\n\n // Index tools\n if (toolsResult?.tools) {\n for (const tool of toolsResult.tools) {\n capabilities.push({\n type: 'tool',\n server: serverName,\n name: tool.name,\n description: tool.description,\n schemaText: extractSchemaText(tool.inputSchema),\n } satisfies IndexedTool);\n }\n }\n\n // Index prompts\n if (promptsResult?.prompts) {\n for (const prompt of promptsResult.prompts) {\n capabilities.push({\n type: 'prompt',\n server: serverName,\n name: prompt.name,\n description: prompt.description,\n argumentsText: extractArgumentsText(prompt.arguments as PromptArgument[] | undefined),\n arguments: prompt.arguments as PromptArgument[] | undefined,\n } satisfies IndexedPrompt);\n }\n }\n\n // Index resources\n if (resourcesResult?.resources) {\n for (const resource of resourcesResult.resources) {\n capabilities.push({\n type: 'resource',\n server: serverName,\n name: resource.name,\n description: resource.description,\n uri: resource.uri,\n mimeType: resource.mimeType,\n } satisfies IndexedResource);\n }\n }\n }\n\n return {\n capabilities,\n servers,\n indexedAt: new Date(),\n };\n}\n\n/**\n * Calculate relevance score and matched fields for a capability against a query\n */\nfunction scoreCapability(capability: IndexedCapability, queryTerms: string[], searchFields: SearchField[]): { score: number; matchedOn: string[] } {\n const matchedOn: string[] = [];\n let totalScore = 0;\n\n // Weights for different match types\n const EXACT_NAME_WEIGHT = 1.0;\n const PARTIAL_NAME_WEIGHT = 0.8;\n const DESCRIPTION_WEIGHT = 0.6;\n const SCHEMA_WEIGHT = 0.4;\n const SERVER_WEIGHT = 0.3;\n\n const nameLower = capability.name.toLowerCase();\n const descLower = (capability.description || '').toLowerCase();\n const serverLower = capability.server.toLowerCase();\n\n // Get schema/arguments text based on type\n let schemaTextLower = '';\n if (capability.type === 'tool') {\n schemaTextLower = capability.schemaText.toLowerCase();\n } else if (capability.type === 'prompt') {\n schemaTextLower = capability.argumentsText.toLowerCase();\n } else if (capability.type === 'resource') {\n // For resources, include URI and mimeType in searchable text\n schemaTextLower = `${capability.uri} ${capability.mimeType || ''}`.toLowerCase();\n }\n\n for (const term of queryTerms) {\n const termLower = term.toLowerCase();\n\n // Check name matches\n if (searchFields.includes('name')) {\n if (nameLower === termLower) {\n totalScore += EXACT_NAME_WEIGHT;\n if (!matchedOn.includes('name')) matchedOn.push('name');\n } else if (nameLower.includes(termLower)) {\n totalScore += PARTIAL_NAME_WEIGHT;\n if (!matchedOn.includes('name')) matchedOn.push('name');\n }\n }\n\n // Check description matches\n if (searchFields.includes('description') && descLower.includes(termLower)) {\n totalScore += DESCRIPTION_WEIGHT;\n if (!matchedOn.includes('description')) matchedOn.push('description');\n }\n\n // Check schema/arguments matches\n if (searchFields.includes('schema') && schemaTextLower.includes(termLower)) {\n totalScore += SCHEMA_WEIGHT;\n const fieldName = capability.type === 'tool' ? 'inputSchema' : capability.type === 'prompt' ? 'arguments' : 'uri';\n if (!matchedOn.includes(fieldName)) matchedOn.push(fieldName);\n }\n\n // Check server name matches\n if (searchFields.includes('server') && serverLower.includes(termLower)) {\n totalScore += SERVER_WEIGHT;\n if (!matchedOn.includes('server')) matchedOn.push('server');\n }\n }\n\n // Normalize score to 0-1 range based on number of terms\n const normalizedScore = queryTerms.length > 0 ? Math.min(1, totalScore / queryTerms.length) : 0;\n\n return { score: normalizedScore, matchedOn };\n}\n\n/**\n * Search for capabilities matching a query string\n */\nexport function searchCapabilities(index: CapabilityIndex, query: string, options: SearchOptions = {}): SearchResponse {\n const { types = DEFAULT_TYPES, servers, searchFields = DEFAULT_SEARCH_FIELDS, limit = DEFAULT_LIMIT, threshold = DEFAULT_THRESHOLD } = options;\n\n // Tokenize query into search terms\n const queryTerms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((term) => term.length > 0);\n\n // If empty query, return empty results\n if (queryTerms.length === 0) {\n return { query, results: [], total: 0 };\n }\n\n // Filter and score capabilities\n const scoredResults: Array<{ capability: IndexedCapability; score: number; matchedOn: string[] }> = [];\n\n for (const capability of index.capabilities) {\n // Filter by type\n if (!types.includes(capability.type)) {\n continue;\n }\n\n // Filter by server\n if (servers && servers.length > 0 && !servers.includes(capability.server)) {\n continue;\n }\n\n // Score the capability\n const { score, matchedOn } = scoreCapability(capability, queryTerms, searchFields);\n\n // Apply threshold filter\n if (score >= threshold && matchedOn.length > 0) {\n scoredResults.push({ capability, score, matchedOn });\n }\n }\n\n // Sort by score descending\n scoredResults.sort((a, b) => b.score - a.score);\n\n // Get total before limiting\n const total = scoredResults.length;\n\n // Apply limit and transform to SearchResult\n const results: SearchResult[] = scoredResults.slice(0, limit).map(({ capability, score, matchedOn }) => ({\n type: capability.type,\n server: capability.server,\n name: capability.name,\n description: capability.description,\n matchedOn,\n score,\n }));\n\n return { query, results, total };\n}\n\n/**\n * Convenience function to search directly from connected clients\n * Builds index and performs search in one call\n */\nexport async function search(clients: Map<string, CapabilityClient>, query: string, options: SearchOptions = {}): Promise<SearchResponse> {\n const index = await buildCapabilityIndex(clients);\n return searchCapabilities(index, query, options);\n}\n"],"names":["DEFAULT_LIMIT","DEFAULT_THRESHOLD","DEFAULT_TYPES","DEFAULT_SEARCH_FIELDS","extractSchemaText","inputSchema","schema","parts","description","push","properties","propName","prop","Object","entries","join","extractArgumentsText","args","Array","isArray","map","arg","name","buildCapabilityIndex","clients","capabilities","servers","serverName","client","toolsResult","promptsResult","resourcesResult","Promise","all","listTools","catch","listPrompts","listResources","tools","tool","type","server","schemaText","prompts","prompt","argumentsText","arguments","resources","resource","uri","mimeType","indexedAt","Date","scoreCapability","capability","queryTerms","searchFields","matchedOn","totalScore","EXACT_NAME_WEIGHT","PARTIAL_NAME_WEIGHT","DESCRIPTION_WEIGHT","SCHEMA_WEIGHT","SERVER_WEIGHT","nameLower","toLowerCase","descLower","serverLower","schemaTextLower","term","termLower","includes","fieldName","normalizedScore","length","Math","min","score","searchCapabilities","index","query","options","types","limit","threshold","split","filter","results","total","scoredResults","sort","a","b","slice","search"],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search types for MCP capability discovery
|
|
3
|
+
*
|
|
4
|
+
* Enables agents to discover tools, prompts, and resources without
|
|
5
|
+
* loading full schemas into context.
|
|
6
|
+
*/
|
|
7
|
+
import type { PromptArgument } from '../connection/types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Types of MCP capabilities that can be searched
|
|
10
|
+
*/
|
|
11
|
+
export type CapabilityType = 'tool' | 'prompt' | 'resource';
|
|
12
|
+
/**
|
|
13
|
+
* Fields that can be searched within capabilities
|
|
14
|
+
*/
|
|
15
|
+
export type SearchField = 'name' | 'description' | 'schema' | 'server';
|
|
16
|
+
/**
|
|
17
|
+
* Options for configuring search behavior
|
|
18
|
+
*/
|
|
19
|
+
export interface SearchOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Filter to specific capability types
|
|
22
|
+
* @default ['tool', 'prompt', 'resource']
|
|
23
|
+
*/
|
|
24
|
+
types?: CapabilityType[];
|
|
25
|
+
/**
|
|
26
|
+
* Filter to specific servers by name
|
|
27
|
+
* @default all servers in config
|
|
28
|
+
*/
|
|
29
|
+
servers?: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Which fields to search within
|
|
32
|
+
* @default ['name', 'description', 'schema']
|
|
33
|
+
*/
|
|
34
|
+
searchFields?: SearchField[];
|
|
35
|
+
/**
|
|
36
|
+
* Maximum number of results to return
|
|
37
|
+
* @default 20
|
|
38
|
+
*/
|
|
39
|
+
limit?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Minimum relevance score (0-1) for results
|
|
42
|
+
* @default 0
|
|
43
|
+
*/
|
|
44
|
+
threshold?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A single search result representing a matched capability
|
|
48
|
+
*/
|
|
49
|
+
export interface SearchResult {
|
|
50
|
+
/** The type of capability */
|
|
51
|
+
type: CapabilityType;
|
|
52
|
+
/** The server that provides this capability */
|
|
53
|
+
server: string;
|
|
54
|
+
/** The name of the capability */
|
|
55
|
+
name: string;
|
|
56
|
+
/** Human-readable description (may be truncated) */
|
|
57
|
+
description: string | undefined;
|
|
58
|
+
/** Which fields matched the search query */
|
|
59
|
+
matchedOn: string[];
|
|
60
|
+
/** Relevance score from 0 (low) to 1 (high) */
|
|
61
|
+
score: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Complete search response
|
|
65
|
+
*/
|
|
66
|
+
export interface SearchResponse {
|
|
67
|
+
/** The original search query */
|
|
68
|
+
query: string;
|
|
69
|
+
/** Matching results, sorted by relevance */
|
|
70
|
+
results: SearchResult[];
|
|
71
|
+
/** Total number of matches before limit was applied */
|
|
72
|
+
total: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Internal representation of a tool for indexing
|
|
76
|
+
*/
|
|
77
|
+
export interface IndexedTool {
|
|
78
|
+
type: 'tool';
|
|
79
|
+
server: string;
|
|
80
|
+
name: string;
|
|
81
|
+
description: string | undefined;
|
|
82
|
+
/** Flattened searchable text from inputSchema property descriptions */
|
|
83
|
+
schemaText: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Internal representation of a prompt for indexing
|
|
87
|
+
*/
|
|
88
|
+
export interface IndexedPrompt {
|
|
89
|
+
type: 'prompt';
|
|
90
|
+
server: string;
|
|
91
|
+
name: string;
|
|
92
|
+
description: string | undefined;
|
|
93
|
+
/** Flattened searchable text from arguments */
|
|
94
|
+
argumentsText: string;
|
|
95
|
+
arguments: PromptArgument[] | undefined;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Internal representation of a resource for indexing
|
|
99
|
+
*/
|
|
100
|
+
export interface IndexedResource {
|
|
101
|
+
type: 'resource';
|
|
102
|
+
server: string;
|
|
103
|
+
name: string;
|
|
104
|
+
description: string | undefined;
|
|
105
|
+
uri: string;
|
|
106
|
+
mimeType: string | undefined;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Union of all indexed capability types
|
|
110
|
+
*/
|
|
111
|
+
export type IndexedCapability = IndexedTool | IndexedPrompt | IndexedResource;
|
|
112
|
+
/**
|
|
113
|
+
* Index containing all capabilities from connected servers
|
|
114
|
+
*/
|
|
115
|
+
export interface CapabilityIndex {
|
|
116
|
+
/** All indexed capabilities */
|
|
117
|
+
capabilities: IndexedCapability[];
|
|
118
|
+
/** Servers that were indexed */
|
|
119
|
+
servers: string[];
|
|
120
|
+
/** When the index was created */
|
|
121
|
+
indexedAt: Date;
|
|
122
|
+
}
|