@cyanmycelium/mcp-broker 1.3.1 → 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/README.md CHANGED
@@ -232,12 +232,22 @@ Each provider entry from `providers_list` / `provider_status`:
232
232
  "name": "weather",
233
233
  "transport": "ws",
234
234
  "connected": true,
235
+ "aggregate": true,
236
+ "connectedSince": "2026-09-14T08:12:03.417Z",
237
+ "connectedForMs": 184211,
235
238
  "clientCount": 0,
236
239
  "sessionCount": 1,
237
240
  "pendingCount": 0
238
241
  }
239
242
  ```
240
243
 
244
+ - The first six fields are about the **provider**: whether something serves
245
+ the slot, over what, whether it is in `_all`, and since when. A
246
+ `connectedSince` that is always a few seconds old is a provider that keeps
247
+ reconnecting.
248
+ - The three counts are about the slot's **callers**. A connected provider that
249
+ nobody is calling right now reads `clientCount: 0, sessionCount: 0,
250
+ pendingCount: 0`; that is not a fault.
241
251
  - `transport` is `ws` (dedicated socket), `ws-multiplex` (shared socket),
242
252
  `stdio` (**any** configured upstream: a child process from `stdioUpstreams`,
243
253
  a `.mcpb` bundle, *or* a remote URL from `mcpServers`), `loopback`
@@ -720,6 +730,30 @@ npm start # node dist/bin.js
720
730
 
721
731
  Requires Node 20.11+.
722
732
 
733
+ ### Running the broker of this checkout
734
+
735
+ `npm start` runs `dist/bin.js`, the code of the working tree once `npm run
736
+ build` has been run (from `node/`, which builds the provider package first).
737
+ It reads `.mcp-broker/config.json` next to `package.json` when there is one;
738
+ that folder is gitignored, so each checkout keeps its own. A config that makes
739
+ the broker reachable from a device on the LAN and serves this package's `web/`
740
+ (the broker explorer at `/demos/broker-explorer/`) from the same port:
741
+
742
+ ```json
743
+ {
744
+ "port": 3000,
745
+ "host": "0.0.0.0",
746
+ "www": { "open": false, "mounts": [{ "urlPrefix": "/", "dir": "../web" }] },
747
+ "allowedOrigins": ["http://localhost:3000", "http://127.0.0.1:3000"]
748
+ }
749
+ ```
750
+
751
+ Paths in the file resolve against the file's own directory, hence `../web`.
752
+ Keep the terminal open: the process lives as long as it does. Without the
753
+ file the defaults apply, `0.0.0.0:3000` and no static mount. Every C sample,
754
+ the roundtrip and the soak in [`c/`](https://github.com/pandaGaume/mcp-broker/tree/main/c)
755
+ target this broker, not an installed one.
756
+
723
757
  ## Releasing
724
758
 
725
759
  The package is published to npm by [`.github/workflows/release-node.yml`](https://github.com/pandaGaume/mcp-broker/blob/main/.github/workflows/release-node.yml), triggered by tags of the form `node-v*`.
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { PACKAGE_NAME, VERSION, loadBrokerConfig, WsTunnelBuilder, loadMcpbBundle, BROKER_AGGREGATE_NAME, BROKER_PROVIDER_NAME, resolveOpenTarget } from './chunk-UOJRDF5R.js';
2
+ import { PACKAGE_NAME, VERSION, loadBrokerConfig, WsTunnelBuilder, loadMcpbBundle, BROKER_AGGREGATE_NAME, BROKER_PROVIDER_NAME, resolveOpenTarget } from './chunk-J5TN5RYU.js';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import open from 'open';
@@ -3753,10 +3753,14 @@ var WsTunnel = class {
3753
3753
  transport = "none";
3754
3754
  connected = false;
3755
3755
  }
3756
+ const since = connected ? state.connectedSinceMs : null;
3756
3757
  return {
3757
3758
  name,
3758
3759
  transport,
3759
3760
  connected,
3761
+ aggregate: this._aggregateServer?.providerNames.includes(name) ?? false,
3762
+ connectedSince: since !== null ? new Date(since).toISOString() : null,
3763
+ connectedForMs: since !== null ? Math.max(0, Date.now() - since) : null,
3760
3764
  clientCount: state.wsClients.size,
3761
3765
  sessionCount: state.sseSessions.size + state.httpSessions.size,
3762
3766
  pendingCount: state.pending.size
@@ -3781,6 +3785,7 @@ var WsTunnel = class {
3781
3785
  }
3782
3786
  const state = this._getOrCreateProviderState(name);
3783
3787
  this._loopbackProviders.set(name, transport);
3788
+ state.connectedSinceMs = Date.now();
3784
3789
  transport.onMessage = (data) => this._routeFromProvider(state, name, data);
3785
3790
  transport.onClose = () => {
3786
3791
  this._loopbackProviders.delete(name);
@@ -3915,9 +3920,10 @@ var WsTunnel = class {
3915
3920
  const state = this._providers.get(cfg.name);
3916
3921
  if (state) this._failProviderDisconnected(state, cfg.name);
3917
3922
  };
3918
- if (cfg.aggregate) {
3919
- upstream.onOpen = () => void this._aggregateServer?.addProvider(cfg.name);
3920
- }
3923
+ upstream.onOpen = () => {
3924
+ this._getOrCreateProviderState(cfg.name).connectedSinceMs = Date.now();
3925
+ if (cfg.aggregate) void this._aggregateServer?.addProvider(cfg.name);
3926
+ };
3921
3927
  this._upstreams.set(cfg.name, upstream);
3922
3928
  upstream.connect();
3923
3929
  };
@@ -4905,6 +4911,7 @@ data: ${data}
4905
4911
  }
4906
4912
  const state = this._getOrCreateProviderState(name);
4907
4913
  state.ws = ws;
4914
+ state.connectedSinceMs = Date.now();
4908
4915
  this._watchProviderSocket(ws);
4909
4916
  if (providerPrincipal) {
4910
4917
  this._logProviderRegistration(providerPrincipal, name, this._slotResourceResolver.resolve(name), true);
@@ -5113,6 +5120,7 @@ data: ${data}
5113
5120
  providerNames.add(name);
5114
5121
  const state2 = this._getOrCreateProviderState(name);
5115
5122
  state2.ws = ws;
5123
+ state2.connectedSinceMs = Date.now();
5116
5124
  if (providerPrincipal) {
5117
5125
  this._logProviderRegistration(providerPrincipal, name, this._slotResourceResolver.resolve(name), true);
5118
5126
  }
@@ -5292,6 +5300,7 @@ data: ${data}
5292
5300
  * close handlers (dedicated WS, multiplexed WS, loopback).
5293
5301
  */
5294
5302
  _failProviderDisconnected(state, name) {
5303
+ state.connectedSinceMs = null;
5295
5304
  for (const entry of state.pending.values()) {
5296
5305
  const error = JSON.stringify({
5297
5306
  jsonrpc: "2.0",
@@ -5322,6 +5331,7 @@ data: ${data}
5322
5331
  if (!state) {
5323
5332
  state = {
5324
5333
  ws: null,
5334
+ connectedSinceMs: null,
5325
5335
  pending: /* @__PURE__ */ new Map(),
5326
5336
  sseSessions: /* @__PURE__ */ new Map(),
5327
5337
  httpSessions: /* @__PURE__ */ new Map(),
@@ -5976,5 +5986,5 @@ function resolveOpenTarget(raw, baseUrl) {
5976
5986
  }
5977
5987
 
5978
5988
  export { AuthError, BROKER_AGGREGATE_NAME, BROKER_GUIDES, BROKER_GUIDE_MIME_TYPE, BROKER_GUIDE_TOPICS, BROKER_GUIDE_URI_PREFIX, BROKER_GUIDE_URI_TEMPLATE, BROKER_PROVIDER_NAME, BROKER_RESERVED_SLOTS, BrokerDiagnoseAdapter, BrokerDiagnoseBehavior, BrokerGuideAdapter, BrokerGuideBehavior, BrokerInfoBehavior, BrokerProvidersBehavior, ConfigPolicyEngine, ConfiguredCapabilityClassifier, DEFAULT_CONFIG_FILENAME, DefaultSlotResourceResolver, HttpAuthGuard, JwtSubjectMapper, JwtTokenValidator, PACKAGE_NAME, RemoteUpstream, ResourcePath, ResourcePathPattern, SharedSecretProviderAuthenticator, StdioUpstream, SubjectMappingError, VERSION, WsTunnel, WsTunnelBuilder, authorizationWithEngine, brokerGrammarKey, brokerGuide, brokerGuideIndex, brokerGuideTopicFromUri, brokerGuideUri, buildJwtAuth, buildResourceMetadata, compileAuthorizationPolicy, compileProviderAllowedResources, diagnoseBroker, hasAuthorizationPolicies, isReservedBrokerSlot, iterAvailableBrokerGrammars, iterBrokerGrammarsFrom, loadBrokerConfig, loadBrokerGrammar, loadMcpbBundle, normalizeProviderAuthentication, providerMayPublish, providerPublishDecision, resolveOpenTarget, scopesOf, startBrokerServer, unzipMcpb, validateCapability };
5979
- //# sourceMappingURL=chunk-UOJRDF5R.js.map
5980
- //# sourceMappingURL=chunk-UOJRDF5R.js.map
5989
+ //# sourceMappingURL=chunk-J5TN5RYU.js.map
5990
+ //# sourceMappingURL=chunk-J5TN5RYU.js.map