@winstonsayno/mcp-gateway 2.3.0 → 2.5.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/CHANGELOG.md +31 -0
- package/README.md +18 -0
- package/dashboard/index.html +42 -2
- package/dist/catalog/index.d.ts +78 -0
- package/dist/catalog/index.d.ts.map +1 -0
- package/dist/catalog/index.js +243 -0
- package/dist/catalog/index.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +29 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/gateway/api.d.ts +14 -0
- package/dist/gateway/api.d.ts.map +1 -1
- package/dist/gateway/api.js +94 -1
- package/dist/gateway/api.js.map +1 -1
- package/dist/gateway/index.d.ts +12 -0
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +76 -3
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/invoker.d.ts +7 -0
- package/dist/gateway/invoker.d.ts.map +1 -1
- package/dist/gateway/invoker.js +17 -1
- package/dist/gateway/invoker.js.map +1 -1
- package/dist/gateway/usage.d.ts +82 -0
- package/dist/gateway/usage.d.ts.map +1 -0
- package/dist/gateway/usage.js +181 -0
- package/dist/gateway/usage.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/types.d.ts +33 -0
- package/dist/utils/types.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [2.5.0] - 2026-10-07
|
|
13
|
+
|
|
14
|
+
Usage quotas and metering export.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- **`quotas.rules`**: tool-call limits per `hour` / `day` / `month` (UTC calendar periods), per client (default, with
|
|
18
|
+
`clients` globs) or per tenant (`per: tenant`, shared pool), optionally for some `servers` / `tools`. Over quota →
|
|
19
|
+
`-32007`; REST `429` + `Retry-After` + quota details. Checked after the policy, before cache and upstream.
|
|
20
|
+
- **Metering**: every tool call is counted in hourly buckets per client, tenant, server and tool (calls, errors,
|
|
21
|
+
duration). `GET /api/v1/usage` exports JSON or CSV (`format=csv`, formula-injection safe) grouped by `client`,
|
|
22
|
+
`tenant`, `server`, `tool`, `hour`, `day`, with `since` / `until` / `client` / `tenant` / `server` filters;
|
|
23
|
+
`quotas.meteringRetentionDays` (35).
|
|
24
|
+
- `GET /api/v1/quotas` — live counters with `resetsAt`. Tenant admins / owners can read their own tenant's usage.
|
|
25
|
+
- Exports: `UsageMeter`, `usageCsv`, `periodBounds`, `ERR_QUOTA_EXCEEDED`.
|
|
26
|
+
|
|
27
|
+
## [2.4.0] - 2026-10-07
|
|
28
|
+
|
|
29
|
+
Upstream catalog / registry with one-click add.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- **`catalog:`** — built-in entries for the reference MCP servers (filesystem, memory, everything,
|
|
33
|
+
sequential-thinking, fetch, git, time, GitHub remote) plus `sources` (JSON files or URLs) merged by id.
|
|
34
|
+
- **One-click install** (`catalog.install: true`, off by default): `POST /api/v1/catalog/:id/install` with `serverId`,
|
|
35
|
+
`env` (process env for stdio, `${VAR}` substitution for remote `url` / `headers`) and `args`; validation of required
|
|
36
|
+
inputs; `409` on id clashes. `DELETE /api/v1/catalog/servers/:id` removes it. Operator keys only.
|
|
37
|
+
- `catalog.serversFile` persists installed servers (atomic write, mode 600) and loads them on start; they survive hot
|
|
38
|
+
reloads; config-file ids win on clashes.
|
|
39
|
+
- **Dashboard**: *Add a server* card on the Servers tab (prompts for id, env and args; shows where an entry is
|
|
40
|
+
installed), also in the demo (read-only).
|
|
41
|
+
- Exports: `Catalog`, `InstalledServers`, `BUILTIN_CATALOG`, `buildServerConfig`, `loadCatalogSource`.
|
|
42
|
+
|
|
12
43
|
## [2.3.0] - 2026-10-07
|
|
13
44
|
|
|
14
45
|
Tenants / workspaces with role-based access control.
|
package/README.md
CHANGED
|
@@ -579,6 +579,24 @@ await gateway.start();
|
|
|
579
579
|
process.on('SIGTERM', () => gateway.stop());
|
|
580
580
|
```
|
|
581
581
|
|
|
582
|
+
## What's New in v2.5
|
|
583
|
+
|
|
584
|
+
| Feature | Description |
|
|
585
|
+
|---------|-------------|
|
|
586
|
+
| **Usage quotas** | per key or per tenant, hour / day / month, scoped by server / tool — `429` + `Retry-After` |
|
|
587
|
+
| **Metering export** | `GET /api/v1/usage` as JSON or CSV, grouped by key / tenant / server / tool / hour / day |
|
|
588
|
+
|
|
589
|
+
Details: [CHANGELOG](CHANGELOG.md).
|
|
590
|
+
|
|
591
|
+
## What's New in v2.4
|
|
592
|
+
|
|
593
|
+
| Feature | Description |
|
|
594
|
+
|---------|-------------|
|
|
595
|
+
| **Upstream catalog** | built-in reference servers + your own JSON catalogs (`catalog.sources`) |
|
|
596
|
+
| **One-click add** | dashboard *Add a server* / `POST /api/v1/catalog/:id/install`, optional persistence (`serversFile`) |
|
|
597
|
+
|
|
598
|
+
Details: [CHANGELOG](CHANGELOG.md).
|
|
599
|
+
|
|
582
600
|
## What's New in v2.3
|
|
583
601
|
|
|
584
602
|
| Feature | Description |
|
package/dashboard/index.html
CHANGED
|
@@ -460,6 +460,10 @@ tbody tr:hover { background: var(--surface-2); }
|
|
|
460
460
|
<div class="page-head"><div><h1 data-i18n="srvTitle">Servers & tools</h1><p data-i18n="srvSub">Upstream MCP servers behind this gateway</p></div>
|
|
461
461
|
<button class="btn sm" id="srvRefresh" type="button"><svg class="spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7"/></svg><span data-i18n="refresh">Refresh</span></button></div>
|
|
462
462
|
<div class="grid servers stagger" id="servers"></div>
|
|
463
|
+
<div class="card" id="catalogCard" hidden style="margin-top:14px">
|
|
464
|
+
<h2><span data-i18n="catalog">Add a server</span><span class="right muted" id="catalogNote"></span></h2>
|
|
465
|
+
<ul class="stream" id="catalog"></ul>
|
|
466
|
+
</div>
|
|
463
467
|
</section>
|
|
464
468
|
|
|
465
469
|
<!-- Playground -->
|
|
@@ -573,7 +577,7 @@ const I18N = {
|
|
|
573
577
|
remember: 'Remember on this device', saveConnect: 'Save & connect', clear: 'Clear', close: 'Close',
|
|
574
578
|
tabOverview: 'Overview', tabServers: 'Servers', tabPlayground: 'Playground', tabHistory: 'History', tabConnect: 'Connect',
|
|
575
579
|
ovTitle: 'Overview', ovSub: 'Live traffic through the gateway', window: 'Time window',
|
|
576
|
-
reqRate: 'Request rate', requests: 'Requests', errors: 'Errors', latency: 'Latency', topTools: 'Top tools', perKey: 'Usage per key', perServer: 'Calls per server', approvals: 'Pending approvals', tenants: 'Workspaces', role: 'Role', members: 'members', roleSaved: 'Role updated', approve: 'Approve', deny: 'Deny', approved: 'Approved', denied: 'Denied', expires: 'expires',
|
|
580
|
+
reqRate: 'Request rate', requests: 'Requests', errors: 'Errors', latency: 'Latency', topTools: 'Top tools', perKey: 'Usage per key', perServer: 'Calls per server', approvals: 'Pending approvals', tenants: 'Workspaces', catalog: 'Add a server', add: 'Add', added: 'Server added', installOff: 'read-only (catalog.install is off)', installedAs: 'installed as', role: 'Role', members: 'members', roleSaved: 'Role updated', approve: 'Approve', deny: 'Deny', approved: 'Approved', denied: 'Denied', expires: 'expires',
|
|
577
581
|
errRate: 'Error rate', liveStream: 'Live requests', pause: 'Pause', health: 'Server health',
|
|
578
582
|
kRpm: 'Requests / min', kP50: 'p50 latency', kP95: 'p95 latency', kErr: 'Error rate', kSrv: 'Servers online',
|
|
579
583
|
kTotal: '{n} in window', kP99: 'p99 {v}', kErrN: '{n} failed', kTools: '{n} tools',
|
|
@@ -618,7 +622,7 @@ const I18N = {
|
|
|
618
622
|
remember: '在此设备上记住', saveConnect: '保存并连接', clear: '清除', close: '关闭',
|
|
619
623
|
tabOverview: '总览', tabServers: '服务器', tabPlayground: '调试台', tabHistory: '历史', tabConnect: '接入',
|
|
620
624
|
ovTitle: '总览', ovSub: '网关实时流量', window: '时间范围',
|
|
621
|
-
reqRate: '请求速率', requests: '请求', errors: '错误', latency: '延迟', topTools: '热门工具', perKey: '各密钥用量', perServer: '各服务器调用', approvals: '待审批调用', tenants: '工作区', role: '角色', members: '成员', roleSaved: '角色已更新', approve: '批准', deny: '拒绝', approved: '已批准', denied: '已拒绝', expires: '到期',
|
|
625
|
+
reqRate: '请求速率', requests: '请求', errors: '错误', latency: '延迟', topTools: '热门工具', perKey: '各密钥用量', perServer: '各服务器调用', approvals: '待审批调用', tenants: '工作区', catalog: '添加服务器', add: '添加', added: '已添加服务器', installOff: '只读(未开启 catalog.install)', installedAs: '已安装为', role: '角色', members: '成员', roleSaved: '角色已更新', approve: '批准', deny: '拒绝', approved: '已批准', denied: '已拒绝', expires: '到期',
|
|
622
626
|
errRate: '错误率', liveStream: '实时请求', pause: '暂停', health: '服务器健康',
|
|
623
627
|
kRpm: '每分钟请求', kP50: 'p50 延迟', kP95: 'p95 延迟', kErr: '错误率', kSrv: '在线服务器',
|
|
624
628
|
kTotal: '窗口内 {n} 次', kP99: 'p99 {v}', kErrN: '{n} 次失败', kTools: '{n} 个工具',
|
|
@@ -1000,6 +1004,41 @@ document.addEventListener('change', async (e) => {
|
|
|
1000
1004
|
loadTenants();
|
|
1001
1005
|
});
|
|
1002
1006
|
|
|
1007
|
+
// Upstream catalog: one-click add of well-known MCP servers (operators; needs catalog.install: true).
|
|
1008
|
+
async function loadCatalog() {
|
|
1009
|
+
const card = $('#catalogCard');
|
|
1010
|
+
let r;
|
|
1011
|
+
try { r = await api('/catalog'); } catch { card.hidden = true; return; }
|
|
1012
|
+
const list = r.entries || [];
|
|
1013
|
+
card.hidden = list.length === 0;
|
|
1014
|
+
state.catalog = r;
|
|
1015
|
+
$('#catalogNote').textContent = r.install ? '' : t('installOff');
|
|
1016
|
+
$('#catalog').innerHTML = list.map((e) => `<li><div style="flex:1;min-width:0"><b>${esc(e.name)}</b> <span class="badge">${esc(e.template.transport)}</span>
|
|
1017
|
+
${(e.installed || []).length ? `<span class="badge ok">${esc(t('installedAs'))} ${esc(e.installed.join(', '))}</span>` : ''}
|
|
1018
|
+
<div class="muted" style="font-size:12.5px">${esc(e.description || '')}${e.homepage ? ` · <a href="${esc(e.homepage)}" target="_blank" rel="noopener noreferrer">docs</a>` : ''}</div></div>
|
|
1019
|
+
${r.install ? `<button class="btn sm primary" type="button" data-install="${esc(e.id)}">${esc(t('add'))}</button>` : ''}</li>`).join('');
|
|
1020
|
+
}
|
|
1021
|
+
document.addEventListener('click', async (e) => {
|
|
1022
|
+
const b = e.target.closest && e.target.closest('[data-install]');
|
|
1023
|
+
if (!b) return;
|
|
1024
|
+
const entry = (state.catalog?.entries || []).find((x) => x.id === b.dataset.install);
|
|
1025
|
+
if (!entry) return;
|
|
1026
|
+
const serverId = prompt('Server id', entry.installed && entry.installed.length ? `${entry.id}-2` : entry.id);
|
|
1027
|
+
if (!serverId) return;
|
|
1028
|
+
const env = {};
|
|
1029
|
+
for (const v of entry.env || []) { const val = prompt(`${v.name}${v.description ? ` — ${v.description}` : ''}`, ''); if (val === null) return; if (val) env[v.name] = val; }
|
|
1030
|
+
const args = [];
|
|
1031
|
+
for (const a of entry.args || []) { const val = prompt(`${a.name}${a.description ? ` — ${a.description}` : ''}`, a.default || ''); if (val === null) return; args.push(val); }
|
|
1032
|
+
b.classList.add('loading');
|
|
1033
|
+
try {
|
|
1034
|
+
await api(`/catalog/${encodeURIComponent(entry.id)}/install`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ serverId, env, args }) });
|
|
1035
|
+
toast(t('added'));
|
|
1036
|
+
loadServers();
|
|
1037
|
+
} catch (err) { toast(err.message || String(err)); }
|
|
1038
|
+
b.classList.remove('loading');
|
|
1039
|
+
loadCatalog();
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1003
1042
|
async function loadServers() {
|
|
1004
1043
|
const btn = $('#srvRefresh'); btn.classList.add('loading');
|
|
1005
1044
|
try {
|
|
@@ -1678,6 +1717,7 @@ reloadAll().then(() => {
|
|
|
1678
1717
|
// Refresh the slower-moving pieces in the background.
|
|
1679
1718
|
setInterval(() => { if (!document.hidden) loadApprovals(); }, 3000);
|
|
1680
1719
|
loadTenants();
|
|
1720
|
+
loadCatalog();
|
|
1681
1721
|
setInterval(() => { if (!document.hidden) loadTenants(); }, 30000);
|
|
1682
1722
|
setInterval(() => { if (!document.hidden) { loadServers(); if (state.live === 'live') loadStats().catch(() => {}); } }, 10000);
|
|
1683
1723
|
document.addEventListener('visibilitychange', () => { if (!document.hidden && state.live !== 'live') startLive(); });
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstream catalog / registry: templates for well-known MCP servers that an
|
|
3
|
+
* operator can add with one click (dashboard) or one request
|
|
4
|
+
* (`POST /api/v1/catalog/:id/install`).
|
|
5
|
+
*
|
|
6
|
+
* The built-in entries cover the reference servers; `catalog.sources` adds
|
|
7
|
+
* entries from JSON files or URLs (`{ "entries": [...] }` or an array).
|
|
8
|
+
* Installing is off unless `catalog.install: true` — it spawns processes.
|
|
9
|
+
* Installed servers are kept in `catalog.serversFile` (JSON) when set, and
|
|
10
|
+
* loaded again on start.
|
|
11
|
+
*
|
|
12
|
+
* @module catalog
|
|
13
|
+
*/
|
|
14
|
+
import type { CatalogConfig, McpServerConfig } from '../utils/types.js';
|
|
15
|
+
export interface CatalogEnvVar {
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
required?: boolean;
|
|
19
|
+
secret?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface CatalogEntry {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
homepage?: string;
|
|
26
|
+
tags?: string[];
|
|
27
|
+
/** Server config template (`id` / `name` come from the install request). */
|
|
28
|
+
template: Omit<McpServerConfig, 'id' | 'name'>;
|
|
29
|
+
/** Environment variables (stdio) or `${VAR}` placeholders the template needs. */
|
|
30
|
+
env?: CatalogEnvVar[];
|
|
31
|
+
/** Extra positional args the user must supply (e.g. allowed directories). */
|
|
32
|
+
args?: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
required?: boolean;
|
|
36
|
+
default?: string;
|
|
37
|
+
}>;
|
|
38
|
+
source?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Built-in catalog (reference MCP servers). */
|
|
41
|
+
export declare const BUILTIN_CATALOG: CatalogEntry[];
|
|
42
|
+
/** Load entries from one source (file path relative to `baseDir`, or http(s) URL). */
|
|
43
|
+
export declare function loadCatalogSource(source: string, baseDir?: string, fetchImpl?: typeof fetch): Promise<CatalogEntry[]>;
|
|
44
|
+
export declare class Catalog {
|
|
45
|
+
private config;
|
|
46
|
+
private readonly baseDir;
|
|
47
|
+
private entries;
|
|
48
|
+
constructor(config: () => CatalogConfig | undefined, baseDir?: () => string | undefined);
|
|
49
|
+
/** (Re)load `catalog.sources`; failures are logged and skipped. */
|
|
50
|
+
refresh(fetchImpl?: typeof fetch): Promise<void>;
|
|
51
|
+
list(): readonly CatalogEntry[];
|
|
52
|
+
get(id: string): CatalogEntry | undefined;
|
|
53
|
+
installEnabled(): boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface InstallRequest {
|
|
56
|
+
/** Server id (default: the entry id). */
|
|
57
|
+
serverId?: string;
|
|
58
|
+
name?: string;
|
|
59
|
+
/** Values for `entry.env` (stdio: process env; remote: `${VAR}` substitution). */
|
|
60
|
+
env?: Record<string, string>;
|
|
61
|
+
/** Values for `entry.args`, in order. */
|
|
62
|
+
args?: string[];
|
|
63
|
+
tags?: string[];
|
|
64
|
+
}
|
|
65
|
+
/** Build a server config from a catalog entry. Throws with a user-facing message on missing input. */
|
|
66
|
+
export declare function buildServerConfig(entry: CatalogEntry, req: InstallRequest): McpServerConfig;
|
|
67
|
+
/** Servers installed from the catalog, persisted in `catalog.serversFile`. */
|
|
68
|
+
export declare class InstalledServers {
|
|
69
|
+
private readonly file;
|
|
70
|
+
private servers;
|
|
71
|
+
constructor(file: () => string | undefined);
|
|
72
|
+
load(): McpServerConfig[];
|
|
73
|
+
list(): readonly McpServerConfig[];
|
|
74
|
+
add(s: McpServerConfig): Promise<void>;
|
|
75
|
+
remove(id: string): Promise<boolean>;
|
|
76
|
+
private save;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/catalog/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGxE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,4EAA4E;IAC5E,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IAC/C,iFAAiF;IACjF,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC;IACtB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAQD,gDAAgD;AAChD,eAAO,MAAM,eAAe,EAAE,YAAY,EAoEzC,CAAC;AAQF,sFAAsF;AACtF,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,SAAgB,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAczI;AAED,qBAAa,OAAO;IAGN,OAAO,CAAC,MAAM;IAAmC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFrF,OAAO,CAAC,OAAO,CAAwC;gBAEnC,MAAM,EAAE,MAAM,aAAa,GAAG,SAAS,EAAmB,OAAO,GAAE,MAAM,MAAM,GAAG,SAA2B;IAEjI,mEAAmE;IAC7D,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBtD,IAAI,IAAI,SAAS,YAAY,EAAE;IAI/B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIzC,cAAc,IAAI,OAAO;CAG1B;AAED,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,sGAAsG;AACtG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,cAAc,GAAG,eAAe,CAgC3F;AAED,8EAA8E;AAC9E,qBAAa,gBAAgB;IAGf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,OAAO,CAAyB;gBAEX,IAAI,EAAE,MAAM,MAAM,GAAG,SAAS;IAE3D,IAAI,IAAI,eAAe,EAAE;IAazB,IAAI,IAAI,SAAS,eAAe,EAAE;IAI5B,GAAG,CAAC,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAKtC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAQ5B,IAAI;CAOnB"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstream catalog / registry: templates for well-known MCP servers that an
|
|
3
|
+
* operator can add with one click (dashboard) or one request
|
|
4
|
+
* (`POST /api/v1/catalog/:id/install`).
|
|
5
|
+
*
|
|
6
|
+
* The built-in entries cover the reference servers; `catalog.sources` adds
|
|
7
|
+
* entries from JSON files or URLs (`{ "entries": [...] }` or an array).
|
|
8
|
+
* Installing is off unless `catalog.install: true` — it spawns processes.
|
|
9
|
+
* Installed servers are kept in `catalog.serversFile` (JSON) when set, and
|
|
10
|
+
* loaded again on start.
|
|
11
|
+
*
|
|
12
|
+
* @module catalog
|
|
13
|
+
*/
|
|
14
|
+
import { readFile, writeFile, rename } from 'fs/promises';
|
|
15
|
+
import { existsSync, readFileSync } from 'fs';
|
|
16
|
+
import { isAbsolute, resolve } from 'path';
|
|
17
|
+
import { logger } from '../utils/logger.js';
|
|
18
|
+
const npx = (pkg, ...extra) => ({
|
|
19
|
+
transport: 'stdio',
|
|
20
|
+
command: 'npx',
|
|
21
|
+
args: ['-y', pkg, ...extra],
|
|
22
|
+
});
|
|
23
|
+
/** Built-in catalog (reference MCP servers). */
|
|
24
|
+
export const BUILTIN_CATALOG = [
|
|
25
|
+
{
|
|
26
|
+
id: 'filesystem',
|
|
27
|
+
name: 'Filesystem',
|
|
28
|
+
description: 'Read, write and search files in allowed directories',
|
|
29
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem',
|
|
30
|
+
tags: ['files'],
|
|
31
|
+
template: npx('@modelcontextprotocol/server-filesystem'),
|
|
32
|
+
args: [{ name: 'directory', description: 'Allowed directory', required: true, default: '/data' }],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'memory',
|
|
36
|
+
name: 'Memory',
|
|
37
|
+
description: 'Knowledge-graph based persistent memory',
|
|
38
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/memory',
|
|
39
|
+
tags: ['memory'],
|
|
40
|
+
template: npx('@modelcontextprotocol/server-memory'),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'everything',
|
|
44
|
+
name: 'Everything (test server)',
|
|
45
|
+
description: 'Reference server exercising every MCP feature',
|
|
46
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/everything',
|
|
47
|
+
tags: ['test'],
|
|
48
|
+
template: npx('@modelcontextprotocol/server-everything'),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'sequential-thinking',
|
|
52
|
+
name: 'Sequential Thinking',
|
|
53
|
+
description: 'Structured step-by-step problem solving',
|
|
54
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking',
|
|
55
|
+
tags: ['reasoning'],
|
|
56
|
+
template: npx('@modelcontextprotocol/server-sequential-thinking'),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'fetch',
|
|
60
|
+
name: 'Fetch',
|
|
61
|
+
description: 'Fetch web pages and convert them to markdown',
|
|
62
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/fetch',
|
|
63
|
+
tags: ['web'],
|
|
64
|
+
template: { transport: 'stdio', command: 'uvx', args: ['mcp-server-fetch'] },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'git',
|
|
68
|
+
name: 'Git',
|
|
69
|
+
description: 'Read, search and manipulate a Git repository',
|
|
70
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/git',
|
|
71
|
+
tags: ['code'],
|
|
72
|
+
template: { transport: 'stdio', command: 'uvx', args: ['mcp-server-git', '--repository'] },
|
|
73
|
+
args: [{ name: 'repository', description: 'Path of the repository', required: true }],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'time',
|
|
77
|
+
name: 'Time',
|
|
78
|
+
description: 'Current time and time-zone conversion',
|
|
79
|
+
homepage: 'https://github.com/modelcontextprotocol/servers/tree/main/src/time',
|
|
80
|
+
tags: ['utility'],
|
|
81
|
+
template: { transport: 'stdio', command: 'uvx', args: ['mcp-server-time'] },
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'github',
|
|
85
|
+
name: 'GitHub',
|
|
86
|
+
description: 'GitHub repositories, issues and pull requests (remote server)',
|
|
87
|
+
homepage: 'https://github.com/github/github-mcp-server',
|
|
88
|
+
tags: ['code', 'remote'],
|
|
89
|
+
template: { transport: 'streamable-http', url: 'https://api.githubcopilot.com/mcp/', headers: { Authorization: 'Bearer ${GITHUB_TOKEN}' } },
|
|
90
|
+
env: [{ name: 'GITHUB_TOKEN', description: 'GitHub personal access token', required: true, secret: true }],
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
function validEntry(e) {
|
|
94
|
+
if (!e || typeof e !== 'object')
|
|
95
|
+
return false;
|
|
96
|
+
const x = e;
|
|
97
|
+
return typeof x.id === 'string' && /^[A-Za-z0-9._-]+$/.test(x.id) && typeof x.name === 'string' && !!x.template && typeof x.template === 'object' && typeof x.template.transport === 'string';
|
|
98
|
+
}
|
|
99
|
+
/** Load entries from one source (file path relative to `baseDir`, or http(s) URL). */
|
|
100
|
+
export async function loadCatalogSource(source, baseDir = process.cwd(), fetchImpl = fetch) {
|
|
101
|
+
let raw;
|
|
102
|
+
if (/^https?:\/\//.test(source)) {
|
|
103
|
+
const r = await fetchImpl(source, { headers: { accept: 'application/json' }, signal: AbortSignal.timeout(10_000) });
|
|
104
|
+
if (!r.ok)
|
|
105
|
+
throw new Error(`HTTP ${r.status}`);
|
|
106
|
+
raw = await r.json();
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
raw = JSON.parse(await readFile(isAbsolute(source) ? source : resolve(baseDir, source), 'utf8'));
|
|
110
|
+
}
|
|
111
|
+
const list = Array.isArray(raw) ? raw : raw?.entries;
|
|
112
|
+
if (!Array.isArray(list))
|
|
113
|
+
throw new Error('expected an array or { "entries": [...] }');
|
|
114
|
+
const ok = list.filter(validEntry).map((e) => ({ ...e, description: e.description ?? '', source }));
|
|
115
|
+
if (ok.length !== list.length)
|
|
116
|
+
logger.warn(`Catalog ${source}: ${list.length - ok.length} invalid entries skipped`);
|
|
117
|
+
return ok;
|
|
118
|
+
}
|
|
119
|
+
export class Catalog {
|
|
120
|
+
config;
|
|
121
|
+
baseDir;
|
|
122
|
+
entries = [...BUILTIN_CATALOG];
|
|
123
|
+
constructor(config, baseDir = () => undefined) {
|
|
124
|
+
this.config = config;
|
|
125
|
+
this.baseDir = baseDir;
|
|
126
|
+
}
|
|
127
|
+
/** (Re)load `catalog.sources`; failures are logged and skipped. */
|
|
128
|
+
async refresh(fetchImpl) {
|
|
129
|
+
const cfg = this.config();
|
|
130
|
+
const out = cfg?.builtins === false ? [] : [...BUILTIN_CATALOG];
|
|
131
|
+
for (const src of cfg?.sources ?? []) {
|
|
132
|
+
try {
|
|
133
|
+
for (const e of await loadCatalogSource(src, this.baseDir(), fetchImpl)) {
|
|
134
|
+
const i = out.findIndex((x) => x.id === e.id);
|
|
135
|
+
if (i >= 0)
|
|
136
|
+
out[i] = e;
|
|
137
|
+
else
|
|
138
|
+
out.push(e);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
logger.warn(`Catalog source ${src} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
this.entries = out;
|
|
146
|
+
}
|
|
147
|
+
list() {
|
|
148
|
+
return this.entries;
|
|
149
|
+
}
|
|
150
|
+
get(id) {
|
|
151
|
+
return this.entries.find((e) => e.id === id);
|
|
152
|
+
}
|
|
153
|
+
installEnabled() {
|
|
154
|
+
return this.config()?.install === true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Build a server config from a catalog entry. Throws with a user-facing message on missing input. */
|
|
158
|
+
export function buildServerConfig(entry, req) {
|
|
159
|
+
const id = req.serverId ?? entry.id;
|
|
160
|
+
if (!/^[A-Za-z0-9._-]+$/.test(id))
|
|
161
|
+
throw new Error('serverId: letters, digits, ".", "_" and "-" only');
|
|
162
|
+
const env = req.env ?? {};
|
|
163
|
+
for (const v of entry.env ?? []) {
|
|
164
|
+
if (v.required && !env[v.name])
|
|
165
|
+
throw new Error(`env.${v.name} is required (${v.description ?? 'see the catalog entry'})`);
|
|
166
|
+
}
|
|
167
|
+
const extra = [];
|
|
168
|
+
(entry.args ?? []).forEach((a, i) => {
|
|
169
|
+
const value = req.args?.[i] ?? a.default;
|
|
170
|
+
if (value === undefined || value === '') {
|
|
171
|
+
if (a.required)
|
|
172
|
+
throw new Error(`args[${i}] (${a.name}) is required`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
extra.push(value);
|
|
176
|
+
});
|
|
177
|
+
const subst = (s) => s.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (m, k) => env[k] ?? m);
|
|
178
|
+
const t = entry.template;
|
|
179
|
+
const cfg = {
|
|
180
|
+
timeout: 30_000,
|
|
181
|
+
maxConcurrency: 10,
|
|
182
|
+
enabled: true,
|
|
183
|
+
...t,
|
|
184
|
+
id,
|
|
185
|
+
name: req.name ?? entry.name,
|
|
186
|
+
tags: [...new Set([...(t.tags ?? []), ...(entry.tags ?? []), ...(req.tags ?? []), 'catalog'])],
|
|
187
|
+
};
|
|
188
|
+
if (extra.length)
|
|
189
|
+
cfg.args = [...(t.args ?? []), ...extra];
|
|
190
|
+
if (t.headers)
|
|
191
|
+
cfg.headers = Object.fromEntries(Object.entries(t.headers).map(([k, v]) => [k, subst(v)]));
|
|
192
|
+
if (t.url)
|
|
193
|
+
cfg.url = subst(t.url);
|
|
194
|
+
if (t.transport === 'stdio' && Object.keys(env).length)
|
|
195
|
+
cfg.env = { ...(t.env ?? {}), ...env };
|
|
196
|
+
return cfg;
|
|
197
|
+
}
|
|
198
|
+
/** Servers installed from the catalog, persisted in `catalog.serversFile`. */
|
|
199
|
+
export class InstalledServers {
|
|
200
|
+
file;
|
|
201
|
+
servers = [];
|
|
202
|
+
constructor(file) {
|
|
203
|
+
this.file = file;
|
|
204
|
+
}
|
|
205
|
+
load() {
|
|
206
|
+
const f = this.file();
|
|
207
|
+
if (!f || !existsSync(f))
|
|
208
|
+
return (this.servers = []);
|
|
209
|
+
try {
|
|
210
|
+
const raw = JSON.parse(readFileSync(f, 'utf8'));
|
|
211
|
+
this.servers = Array.isArray(raw.servers) ? raw.servers : [];
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
logger.error(`Cannot read catalog.serversFile ${f}: ${err instanceof Error ? err.message : String(err)}`);
|
|
215
|
+
this.servers = [];
|
|
216
|
+
}
|
|
217
|
+
return this.servers;
|
|
218
|
+
}
|
|
219
|
+
list() {
|
|
220
|
+
return this.servers;
|
|
221
|
+
}
|
|
222
|
+
async add(s) {
|
|
223
|
+
this.servers = [...this.servers.filter((x) => x.id !== s.id), s];
|
|
224
|
+
await this.save();
|
|
225
|
+
}
|
|
226
|
+
async remove(id) {
|
|
227
|
+
const before = this.servers.length;
|
|
228
|
+
this.servers = this.servers.filter((x) => x.id !== id);
|
|
229
|
+
if (this.servers.length === before)
|
|
230
|
+
return false;
|
|
231
|
+
await this.save();
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
async save() {
|
|
235
|
+
const f = this.file();
|
|
236
|
+
if (!f)
|
|
237
|
+
return;
|
|
238
|
+
const tmp = `${f}.tmp`;
|
|
239
|
+
await writeFile(tmp, JSON.stringify({ servers: this.servers }, null, 2) + '\n', { mode: 0o600 });
|
|
240
|
+
await rename(tmp, f);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/catalog/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AA8hBvD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMzE;AAED,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAwB5E;AAyCD,wBAAgB,qBAAqB,IAAI,MAAM,CAiH9C"}
|
package/dist/config/loader.js
CHANGED
|
@@ -399,6 +399,35 @@ const GatewayConfigSchema = z.object({
|
|
|
399
399
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: bad });
|
|
400
400
|
})
|
|
401
401
|
.optional(),
|
|
402
|
+
quotas: z
|
|
403
|
+
.object({
|
|
404
|
+
rules: z
|
|
405
|
+
.array(z
|
|
406
|
+
.object({
|
|
407
|
+
name: z.string().min(1).optional(),
|
|
408
|
+
limit: z.number().int().min(0),
|
|
409
|
+
period: z.enum(['hour', 'day', 'month']),
|
|
410
|
+
per: z.enum(['client', 'tenant']).optional(),
|
|
411
|
+
clients: z.array(z.string().min(1)).optional(),
|
|
412
|
+
tenants: z.array(z.string().min(1)).optional(),
|
|
413
|
+
servers: z.array(z.string().min(1)).optional(),
|
|
414
|
+
tools: z.array(z.string().min(1)).optional(),
|
|
415
|
+
})
|
|
416
|
+
.strict())
|
|
417
|
+
.optional(),
|
|
418
|
+
meteringRetentionDays: z.number().int().positive().optional(),
|
|
419
|
+
})
|
|
420
|
+
.strict()
|
|
421
|
+
.optional(),
|
|
422
|
+
catalog: z
|
|
423
|
+
.object({
|
|
424
|
+
builtins: z.boolean().optional(),
|
|
425
|
+
sources: z.array(z.string().min(1)).optional(),
|
|
426
|
+
install: z.boolean().optional(),
|
|
427
|
+
serversFile: z.string().min(1).optional(),
|
|
428
|
+
})
|
|
429
|
+
.strict()
|
|
430
|
+
.optional(),
|
|
402
431
|
tenants: z
|
|
403
432
|
.array(z
|
|
404
433
|
.object({
|