@ni-c/mcp-hub 0.6.2 → 0.6.4
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 +42 -0
- package/README.md +9 -1
- package/dist/proxy.js +22 -1
- package/dist/proxy.js.map +1 -1
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.4] - 2026-08-18
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- The architecture diagram no longer depends on the reader's operating system.
|
|
13
|
+
It carried a `prefers-color-scheme` block, which resolves against the OS rather
|
|
14
|
+
than the theme toggle of GitHub or npm — so dark-mode readers on a light OS got
|
|
15
|
+
the light artwork on a dark page. The README now uses `<picture>`, which is
|
|
16
|
+
resolved against the page, and the `<img>` that npm falls back to brings its own
|
|
17
|
+
card instead of a media query.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- The diagram is generated from a single source, `docs/assets/architecture.source.svg`,
|
|
22
|
+
by `npm run assets`. The four rendered copies had already drifted apart; CI now
|
|
23
|
+
fails if one of them is edited by hand.
|
|
24
|
+
- `docs/public/og.png` is generated at exactly 1280x640, GitHub's recommended size
|
|
25
|
+
for a social preview, instead of being drawn by hand.
|
|
26
|
+
|
|
27
|
+
## [0.6.3] - 2026-08-17
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- The per-server proxy no longer advertises resource subscriptions it cannot
|
|
32
|
+
serve. It passed the child's capabilities through unchanged, so a server that
|
|
33
|
+
supports `resources/subscribe` made the hub claim it too — while the proxy
|
|
34
|
+
registers no handler for it, and the call failed with `-32601`. Only the
|
|
35
|
+
`subscribe` flag is dropped: listing, templates and reading are unaffected.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Known gaps: the entry claiming `RESOURCE_BOUND_TOKENS` still defaults to
|
|
40
|
+
`false` is gone. Resource binding has been the default since 0.5.0, and every
|
|
41
|
+
other document already said so — this was the one place still describing the
|
|
42
|
+
old behaviour and promising a flip that had already happened.
|
|
43
|
+
- Known gaps now state that `listChanged` is announced but never sent. Passing
|
|
44
|
+
it on is deliberate: delivering server-initiated messages needs the per-client
|
|
45
|
+
session state the stateless transport exists to avoid, and a client waiting
|
|
46
|
+
for a notification that never comes is no worse off than one that was never
|
|
47
|
+
told.
|
|
48
|
+
|
|
8
49
|
## [0.6.2] - 2026-08-17
|
|
9
50
|
|
|
10
51
|
### Fixed
|
|
@@ -127,6 +168,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
127
168
|
`RESOURCE_BOUND_TOKENS=false` — it restores the old behaviour and logs a
|
|
128
169
|
warning on every start. The default also applies to `createHub()` for
|
|
129
170
|
programmatic use.
|
|
171
|
+
|
|
130
172
|
- **Breaking: `/health` requires a token for `/hub`.** It reports the same
|
|
131
173
|
fleet-wide view as the aggregate — every server's name, state and tool count —
|
|
132
174
|
so a token bound to a single server no longer reads it. Unauthenticated
|
package/README.md
CHANGED
|
@@ -16,7 +16,15 @@ protected by a single password, plus long-lived API tokens for clients that
|
|
|
16
16
|
cannot do OAuth (OpenAI Responses API, xAI API, Gemini API). Per-client
|
|
17
17
|
recipes: [client compatibility](https://mcp-hub.ni-c.de/guide/client-compatibility).
|
|
18
18
|
|
|
19
|
-
<
|
|
19
|
+
<!-- <picture> is resolved against the colour scheme of the page showing it, so GitHub
|
|
20
|
+
picks the variant that matches its own theme toggle. npm strips <picture> and
|
|
21
|
+
<source> when it sanitises the README and keeps the <img>, which is why that
|
|
22
|
+
fallback brings its own dark card instead of relying on a media query. -->
|
|
23
|
+
<picture>
|
|
24
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://mcp-hub.ni-c.de/architecture-dark.svg">
|
|
25
|
+
<source media="(prefers-color-scheme: light)" srcset="https://mcp-hub.ni-c.de/architecture-light.svg">
|
|
26
|
+
<img src="https://mcp-hub.ni-c.de/architecture.svg" alt="MCP clients connect through a reverse proxy to mcp-hub: one Node process with an OAuth 2.1 authorization server, one path per server plus the /hub aggregate, and a supervisor keeping the stdio children and remote upstreams alive" width="800">
|
|
27
|
+
</picture>
|
|
20
28
|
|
|
21
29
|

|
|
22
30
|
|
package/dist/proxy.js
CHANGED
|
@@ -2,6 +2,27 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
2
2
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
3
|
import { CallToolRequestSchema, CallToolResultSchema, CompleteRequestSchema, CompleteResultSchema, GetPromptRequestSchema, GetPromptResultSchema, ListPromptsRequestSchema, ListPromptsResultSchema, ListResourcesRequestSchema, ListResourcesResultSchema, ListResourceTemplatesRequestSchema, ListResourceTemplatesResultSchema, ListToolsRequestSchema, ListToolsResultSchema, ReadResourceRequestSchema, ReadResourceResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
4
|
const CALL_TIMEOUT_MS = 5 * 60_000;
|
|
5
|
+
/**
|
|
6
|
+
* The child's capabilities minus what this proxy does not actually serve.
|
|
7
|
+
*
|
|
8
|
+
* resources.subscribe is dropped: there is no Subscribe handler below, so a
|
|
9
|
+
* client that believed the advertisement got -32601 at call time. Announcing
|
|
10
|
+
* only what we answer is the difference between a missing feature and a lie.
|
|
11
|
+
*
|
|
12
|
+
* listChanged deliberately stays. Forwarding server-initiated messages needs
|
|
13
|
+
* per-client session state, which the stateless transport exists to avoid, so
|
|
14
|
+
* the notification never arrives — but a client waiting for one that never
|
|
15
|
+
* comes is no worse off than a client that was never told. It is listed under
|
|
16
|
+
* the known gaps in the documentation instead.
|
|
17
|
+
*/
|
|
18
|
+
function advertisedCapabilities(capabilities) {
|
|
19
|
+
const caps = { ...(capabilities ?? {}) };
|
|
20
|
+
if (caps.resources) {
|
|
21
|
+
const { subscribe: _subscribe, ...resources } = caps.resources;
|
|
22
|
+
caps.resources = resources;
|
|
23
|
+
}
|
|
24
|
+
return caps;
|
|
25
|
+
}
|
|
5
26
|
/**
|
|
6
27
|
* Builds a per-request MCP Server that forwards every request verbatim to the
|
|
7
28
|
* shared child client. A fresh Server per HTTP request plus a stateless
|
|
@@ -9,7 +30,7 @@ const CALL_TIMEOUT_MS = 5 * 60_000;
|
|
|
9
30
|
* every few minutes without ever closing sessions, so anything stateful leaks.
|
|
10
31
|
*/
|
|
11
32
|
function buildProxyServer(managed) {
|
|
12
|
-
const server = new Server({ name: managed.serverInfo?.name ?? managed.name, version: managed.serverInfo?.version ?? '0.0.0' }, { capabilities: managed.capabilities
|
|
33
|
+
const server = new Server({ name: managed.serverInfo?.name ?? managed.name, version: managed.serverInfo?.version ?? '0.0.0' }, { capabilities: advertisedCapabilities(managed.capabilities) });
|
|
13
34
|
const forward = (request, resultSchema) => {
|
|
14
35
|
const client = managed.client;
|
|
15
36
|
if (!client)
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,yBAAyB,EACzB,kCAAkC,EAClC,iCAAiC,EACjC,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ni-c/mcp-hub",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Serve multiple stdio MCP servers from one container: Claude-Code-style mcpServers config, path-based routing, hub meta-tools, and OAuth 2.1 + API tokens for ChatGPT, Claude and any Streamable-HTTP MCP client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
"test": "vitest run",
|
|
44
44
|
"test:coverage": "vitest run --coverage",
|
|
45
45
|
"test:watch": "vitest",
|
|
46
|
-
"prepublishOnly": "npm test && npm run build"
|
|
46
|
+
"prepublishOnly": "npm test && npm run build",
|
|
47
|
+
"assets": "node scripts/gen-assets.mjs",
|
|
48
|
+
"assets:check": "node scripts/gen-assets.mjs --check"
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
49
51
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
@@ -54,6 +56,7 @@
|
|
|
54
56
|
},
|
|
55
57
|
"devDependencies": {
|
|
56
58
|
"@modelcontextprotocol/server-everything": "^2026.7.4",
|
|
59
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
57
60
|
"@types/express": "^5.0.0",
|
|
58
61
|
"@types/node": "^26.2.0",
|
|
59
62
|
"@types/supertest": "^7.2.1",
|