@eznix/mcp-gateway 1.3.3

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/src/search.ts ADDED
@@ -0,0 +1,94 @@
1
+ import MiniSearch from "minisearch";
2
+ import type { ToolCatalogEntry, SearchFilters, SearchResult } from "./types.js";
3
+
4
+ export class SearchEngine {
5
+ private miniSearch: MiniSearch<ToolCatalogEntry> | null = null;
6
+ private catalog: Map<string, ToolCatalogEntry> = new Map();
7
+ private indexDirty = true;
8
+
9
+ constructor() {}
10
+
11
+ updateCatalog(tools: ToolCatalogEntry[]) {
12
+ this.catalog.clear();
13
+ for (const tool of tools) {
14
+ this.catalog.set(tool.id, tool);
15
+ }
16
+ this.indexDirty = true;
17
+ }
18
+
19
+ addTool(tool: ToolCatalogEntry) {
20
+ this.catalog.set(tool.id, tool);
21
+ this.indexDirty = true;
22
+ }
23
+
24
+ removeTool(id: string) {
25
+ this.catalog.delete(id);
26
+ this.indexDirty = true;
27
+ }
28
+
29
+ getTools(): ToolCatalogEntry[] {
30
+ return Array.from(this.catalog.values());
31
+ }
32
+
33
+ getTool(id: string): ToolCatalogEntry | undefined {
34
+ return this.catalog.get(id);
35
+ }
36
+
37
+ private ensureIndex() {
38
+ if (!this.indexDirty && this.miniSearch) return;
39
+
40
+ const tools = Array.from(this.catalog.values());
41
+
42
+ if (tools.length === 0) {
43
+ this.miniSearch = null;
44
+ this.indexDirty = false;
45
+ return;
46
+ }
47
+
48
+ this.miniSearch = new MiniSearch<ToolCatalogEntry>({
49
+ fields: ["name", "title", "description", "server"],
50
+ storeFields: ["id", "server", "name", "title", "description", "inputSchema", "outputSchema"],
51
+ searchOptions: {
52
+ boost: { name: 3, title: 2 },
53
+ fuzzy: 0.2,
54
+ prefix: true,
55
+ combineWith: "OR",
56
+ },
57
+ });
58
+
59
+ this.miniSearch.addAll(tools);
60
+ this.indexDirty = false;
61
+ }
62
+
63
+ search(query: string, filters: SearchFilters = {}, limit = 50): SearchResult[] {
64
+ this.ensureIndex();
65
+
66
+ if (!this.miniSearch || !query.trim()) {
67
+ return [];
68
+ }
69
+
70
+ const maxLimit = Math.min(limit, 50);
71
+ const results = this.miniSearch.search(query.toLowerCase()).slice(0, 100);
72
+
73
+ const filtered = results
74
+ .filter((result) => {
75
+ if (filters.server && result.server !== filters.server) return false;
76
+ return true;
77
+ })
78
+ .map((result) => ({
79
+ id: result.id,
80
+ server: result.server,
81
+ name: result.name,
82
+ description: result.description,
83
+ score: result.score || 0,
84
+ }))
85
+ .sort((a, b) => b.score - a.score)
86
+ .slice(0, maxLimit);
87
+
88
+ return filtered;
89
+ }
90
+
91
+ warmup() {
92
+ this.ensureIndex();
93
+ }
94
+ }
package/src/types.ts ADDED
@@ -0,0 +1,51 @@
1
+ export interface UpstreamConfig {
2
+ type: "local" | "remote";
3
+ command?: string[];
4
+ url?: string;
5
+ transport?: "streamable_http" | "websocket";
6
+ endpoint?: string;
7
+ enabled?: boolean;
8
+ lazy?: boolean; // if true, only connect on first request
9
+ idleTimeout?: number; // milliseconds before sleeping (default: 2hrs)
10
+ }
11
+
12
+ export interface GatewayConfig {
13
+ [serverKey: string]: UpstreamConfig;
14
+ }
15
+
16
+ export interface ToolCatalogEntry {
17
+ id: string;
18
+ server: string;
19
+ name: string;
20
+ title?: string;
21
+ description?: string;
22
+ inputSchema?: any;
23
+ outputSchema?: any;
24
+ }
25
+
26
+ export interface SearchFilters {
27
+ server?: string;
28
+ tags?: string[];
29
+ }
30
+
31
+ export interface JobRecord {
32
+ id: string;
33
+ status: "queued" | "running" | "completed" | "failed";
34
+ toolId: string;
35
+ args: any;
36
+ priority?: number;
37
+ createdAt: number;
38
+ startedAt?: number;
39
+ finishedAt?: number;
40
+ result?: any;
41
+ error?: string;
42
+ logs: string[];
43
+ }
44
+
45
+ export interface SearchResult {
46
+ id: string;
47
+ server: string;
48
+ name: string;
49
+ description?: string;
50
+ score: number;
51
+ }
@@ -0,0 +1,187 @@
1
+ # MCP Gateway System Guide
2
+
3
+ You are connected to an MCP Gateway that provides unified access to multiple tool servers. Follow these patterns to efficiently discover and use tools without hitting context limits.
4
+
5
+ ## Core Principle
6
+
7
+ **NEVER list all available tools.** The gateway exposes tools from multiple servers (50+ total). Instead, use search-describe-invoke workflow.
8
+
9
+ ## Available Gateway Tools
10
+
11
+ The gateway provides these tools for tool discovery and execution:
12
+
13
+ | Tool | Purpose |
14
+ |------|---------|
15
+ | `gateway.search` | Find relevant tools with BM25 scoring and fuzzy matching |
16
+ | `gateway.describe` | Get full schema for a specific tool |
17
+ | `gateway.invoke` | Execute a tool synchronously |
18
+ | `gateway.invoke_async` | Execute with job queue for long-running operations |
19
+ | `gateway.invoke_status` | Check async job status |
20
+
21
+ ## Three-Step Workflow
22
+
23
+ ### 1. Search for Tools
24
+
25
+ Use `gateway.search` to find what you need:
26
+
27
+ ```json
28
+ {
29
+ "query": "kubernetes pods list",
30
+ "limit": 5,
31
+ "filters": { "server": "kubernetes" }
32
+ }
33
+ ```
34
+
35
+ **Search Features:**
36
+ - **BM25 scoring**: Exact matches get highest scores
37
+ - **Fuzzy matching**: Handles typos ("kubenetes" → finds kubernetes)
38
+ - **Prefix search**: "pod" matches "pods_list"
39
+ - **Field boosting**: Name matches (3x), title matches (2x)
40
+
41
+ **Search Examples:**
42
+ - Kubernetes resources? → `"kubernetes pods list"`
43
+ - GitHub issues? → `"github issue create"`
44
+ - Browser automation? → `"browser navigate url"`
45
+ - Documentation lookup? → `"context7 react hooks"`
46
+
47
+ ### 2. Describe Tool Schema
48
+
49
+ Use `gateway.describe` with the tool ID from search results:
50
+
51
+ ```json
52
+ {
53
+ "id": "kubernetes::pods_list"
54
+ }
55
+ ```
56
+
57
+ This returns the complete schema including all parameters, types, and descriptions.
58
+
59
+ ### 3. Invoke the Tool
60
+
61
+ Use `gateway.invoke` or `gateway.invoke_async`:
62
+
63
+ ```json
64
+ {
65
+ "id": "kubernetes::pods_list",
66
+ "args": { "namespace": "default" },
67
+ "timeoutMs": 30000
68
+ }
69
+ ```
70
+
71
+ For long-running operations:
72
+
73
+ ```json
74
+ {
75
+ "id": "some-server::long-running-task",
76
+ "args": { "param": "value" },
77
+ "priority": 10,
78
+ "timeoutMs": 60000
79
+ }
80
+ // Returns: { "jobId": "job_123456789_abc123" }
81
+ ```
82
+
83
+ Check status with `gateway.invoke_status`:
84
+
85
+ ```json
86
+ {
87
+ "jobId": "job_123456789_abc123"
88
+ }
89
+ ```
90
+
91
+ ## Tool ID Format
92
+
93
+ All tools use `serverKey::toolName` format:
94
+ - `kubernetes::pods_list`
95
+ - `playwright::browser_navigate`
96
+ - `github::search_code`
97
+ - `server-name::tool_name`
98
+
99
+ The `serverKey` is defined in the gateway configuration, not the original server name. Available servers depend on the gateway configuration.
100
+
101
+ ## Common Patterns
102
+
103
+ ### Pattern: Quick Single Tool Use
104
+
105
+ ```
106
+ 1. Search: "slack send message" (limit: 3)
107
+ 2. Describe: "slack::post_message"
108
+ 3. Invoke: {"channel": "#general", "text": "Hello"}
109
+ ```
110
+
111
+ ### Pattern: Exploring Related Tools
112
+
113
+ ```
114
+ 1. Search: "kubernetes pods" (limit: 10)
115
+ 2. Review results, identify the one you need
116
+ 3. Describe: "kubernetes::pods_list"
117
+ 4. Invoke: with required args
118
+ ```
119
+
120
+ ### Pattern: Multi-Step Workflow
121
+
122
+ ```
123
+ 1. Search: "github repo list" (limit: 5)
124
+ 2. Invoke: list_repos, note repo name
125
+ 3. Search: "github issue create" (limit: 5)
126
+ 4. Invoke: create_issue using repo from step 2
127
+ ```
128
+
129
+ ### Pattern: Long-Running Operation
130
+
131
+ ```
132
+ 1. Invoke async: {"priority": 10}
133
+ 2. Response: {"jobId": "job_123456789_abc123"}
134
+ 3. Poll: gateway.invoke_status until completed
135
+ ```
136
+
137
+ ## Best Practices
138
+
139
+ **DO:**
140
+ - Start every task by searching for relevant tools
141
+ - Use `limit` (typically 3-10) to control results
142
+ - Describe tools before invoking to verify parameters
143
+ - Use specific, action-oriented search terms
144
+ - Filter by `server` when you know the source
145
+
146
+ **DON'T:**
147
+ - Never search for generic terms - be specific ("kubernetes pods" not "kubernetes")
148
+ - Don't guess tool parameters - always describe first
149
+ - Don't invoke without confirming the schema
150
+ - There's no `list_tools` tool - use search instead
151
+
152
+ ## Search Tips
153
+
154
+ | Task | Bad Search | Good Search |
155
+ |------|-----------|-------------|
156
+ | List pods | "kubernetes" | "kubernetes pods list" |
157
+ | Create issue | "github" | "github issue create" |
158
+ | Navigate URL | "browser" | "browser navigate url" |
159
+ | Get component | "button" | "component button" |
160
+
161
+ ## Timeout Guidelines
162
+
163
+ - Default: 30000ms (30 seconds)
164
+ - Quick ops (list, get): 10000ms
165
+ - Long ops (deploy, build): 60000ms+
166
+ - Async jobs: no timeout, poll with `invoke_status`
167
+
168
+ ## Error Recovery
169
+
170
+ If a tool invocation fails:
171
+ 1. Describe the tool again to verify parameters
172
+ 2. Check error message for missing/invalid arguments
173
+ 3. Search for alternative tools if needed
174
+ 4. Adjust timeout if operation timed out
175
+ 5. For async jobs, check status for progress
176
+
177
+ ## Remember
178
+
179
+ - You only need 1-3 tools for most tasks
180
+ - Search results are ranked by relevance (BM25)
181
+ - Tool IDs use `serverKey::toolName` format
182
+ - Always describe before invoking
183
+ - Use reasonable limits (3-10 results)
184
+ - The gateway handles authentication and routing to upstream servers
185
+ - Fuzzy matching handles typos automatically
186
+
187
+ This approach keeps your context focused on the specific tools needed for the current task.
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }