@yawlabs/mcp-connect 0.1.1 → 0.1.2

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.
Files changed (3) hide show
  1. package/README.md +174 -0
  2. package/dist/index.js +41 -5
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # @yawlabs/mcp-connect
2
+
3
+ One install. All your MCP servers. Managed from the cloud.
4
+
5
+ mcp-connect is an MCP server that orchestrates all your other MCP servers. Configure your servers once on [mcp.hosting](https://mcp.hosting), install mcp-connect in your client, and never hand-edit MCP JSON configs again.
6
+
7
+ ## How it works
8
+
9
+ ```
10
+ Your MCP client (Claude Code, Cursor, etc.)
11
+ |
12
+ | single stdio connection
13
+ v
14
+ @yawlabs/mcp-connect
15
+ | | |
16
+ v v v
17
+ GitHub Slack Stripe ← your MCP servers (local or remote)
18
+ ```
19
+
20
+ 1. You add servers on [mcp.hosting](https://mcp.hosting) (name, command, args, env vars)
21
+ 2. mcp-connect pulls your config on startup
22
+ 3. You use 3 meta-tools to control which servers are active:
23
+ - **`mcp_connect_discover`** — list all configured servers
24
+ - **`mcp_connect_activate`** — connect a server and load its tools
25
+ - **`mcp_connect_deactivate`** — disconnect and remove tools
26
+
27
+ Only activated servers load tools into context. This keeps your context window clean.
28
+
29
+ ## Install
30
+
31
+ ### Claude Code
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "mcp-connect": {
37
+ "command": "npx",
38
+ "args": ["-y", "@yawlabs/mcp-connect"],
39
+ "env": {
40
+ "MCP_HOSTING_TOKEN": "mcp_pat_your_token_here"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Cursor / VS Code
48
+
49
+ Add to your MCP settings:
50
+
51
+ ```json
52
+ {
53
+ "mcp-connect": {
54
+ "command": "npx",
55
+ "args": ["-y", "@yawlabs/mcp-connect"],
56
+ "env": {
57
+ "MCP_HOSTING_TOKEN": "mcp_pat_your_token_here"
58
+ }
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### Claude Desktop
64
+
65
+ Add to `claude_desktop_config.json`:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "mcp-connect": {
71
+ "command": "npx",
72
+ "args": ["-y", "@yawlabs/mcp-connect"],
73
+ "env": {
74
+ "MCP_HOSTING_TOKEN": "mcp_pat_your_token_here"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Getting your token
82
+
83
+ 1. Sign up at [mcp.hosting](https://mcp.hosting)
84
+ 2. Go to **Settings > API Tokens**
85
+ 3. Create a token — it starts with `mcp_pat_`
86
+ 4. Add it to your MCP client config as shown above
87
+
88
+ ## Adding servers
89
+
90
+ On [mcp.hosting](https://mcp.hosting), add each MCP server you want to orchestrate:
91
+
92
+ | Field | Description |
93
+ |-------|-------------|
94
+ | **Name** | Display name (e.g., "GitHub") |
95
+ | **Namespace** | Short prefix for tool names (e.g., "gh") |
96
+ | **Type** | `local` (stdio) or `remote` (HTTP) |
97
+ | **Command** | For local: the command to run (e.g., "npx") |
98
+ | **Args** | For local: command arguments (e.g., ["-y", "@modelcontextprotocol/server-github"]) |
99
+ | **Env** | Environment variables (API keys, tokens) |
100
+ | **URL** | For remote: the server URL |
101
+
102
+ ## Usage
103
+
104
+ Once configured, your LLM will see three tools. Here's the typical flow:
105
+
106
+ ### 1. Discover servers
107
+
108
+ ```
109
+ > What MCP servers do I have?
110
+
111
+ Available MCP servers:
112
+
113
+ gh — GitHub [available] (local)
114
+ slack — Slack [available] (local)
115
+ stripe — Stripe [available] (local)
116
+
117
+ 0 active, 0 tools loaded.
118
+ ```
119
+
120
+ ### 2. Activate what you need
121
+
122
+ ```
123
+ > Activate my GitHub server
124
+
125
+ Activated "gh" — 24 tools available: gh_create_issue, gh_list_prs, ...
126
+ ```
127
+
128
+ The tool list updates automatically via `tools/list_changed`. Your client will see the new tools immediately.
129
+
130
+ ### 3. Use the tools
131
+
132
+ ```
133
+ > List my open PRs
134
+
135
+ [gh_list_prs is called, returns results]
136
+ ```
137
+
138
+ Tools are namespaced: `{namespace}_{original_tool_name}`. This prevents collisions between servers.
139
+
140
+ ### 4. Deactivate when done
141
+
142
+ ```
143
+ > Deactivate GitHub
144
+
145
+ Deactivated "gh". Tools removed.
146
+ ```
147
+
148
+ This frees up context for other tools.
149
+
150
+ ## Config sync
151
+
152
+ mcp-connect polls [mcp.hosting](https://mcp.hosting) every 60 seconds for config changes. When you add, remove, or modify a server on the dashboard, mcp-connect picks it up automatically — no restart needed.
153
+
154
+ ## Environment variables
155
+
156
+ | Variable | Required | Description |
157
+ |----------|----------|-------------|
158
+ | `MCP_HOSTING_TOKEN` | Yes | Your personal access token from mcp.hosting |
159
+ | `MCP_HOSTING_URL` | No | API URL (default: `https://mcp.hosting`) |
160
+
161
+ ## Requirements
162
+
163
+ - Node.js 18+
164
+ - An [mcp.hosting](https://mcp.hosting) account
165
+
166
+ ## License
167
+
168
+ MIT
169
+
170
+ ## Links
171
+
172
+ - [mcp.hosting](https://mcp.hosting) — Dashboard and server management
173
+ - [@yawlabs/mcp-compliance](https://www.npmjs.com/package/@yawlabs/mcp-compliance) — Test your MCP servers for spec compliance
174
+ - [GitHub](https://github.com/YawLabs/mcp-connect) — Source code and issues
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
56
56
  var META_TOOLS = {
57
57
  discover: {
58
58
  name: "mcp_connect_discover",
59
- description: "List all available MCP servers configured in your mcp.hosting account. Shows server names, namespaces, types, and whether they are currently active. Call this first to see what servers are available before activating them.",
59
+ description: "List all available MCP servers. Call this FIRST before activating anything. Only activate servers you need for the CURRENT task \u2014 each one adds tools to your context. Shows server names, namespaces, tool counts, and activation status.",
60
60
  inputSchema: {
61
61
  type: "object",
62
62
  properties: {}
@@ -71,7 +71,7 @@ var META_TOOLS = {
71
71
  },
72
72
  activate: {
73
73
  name: "mcp_connect_activate",
74
- description: 'Activate an MCP server by its namespace. This connects to the server and makes its tools available. After activation, the tool list will update with the new tools prefixed by the namespace (e.g., "gh_create_issue" for namespace "gh").',
74
+ description: 'Activate an MCP server by namespace to load its tools. Each server adds tools to context, so only activate what you need right now. Good practice: deactivate servers you are done with before activating new ones. Tools are prefixed by namespace (e.g., "gh_create_issue").',
75
75
  inputSchema: {
76
76
  type: "object",
77
77
  properties: {
@@ -92,7 +92,7 @@ var META_TOOLS = {
92
92
  },
93
93
  deactivate: {
94
94
  name: "mcp_connect_deactivate",
95
- description: "Deactivate an MCP server by its namespace. This disconnects from the server and removes its tools from the available tool list. Use this to free up context when you no longer need a server.",
95
+ description: "Deactivate an MCP server to remove its tools and free context. Always deactivate servers you are finished with. Servers idle for 10+ tool calls to other servers are auto-deactivated.",
96
96
  inputSchema: {
97
97
  type: "object",
98
98
  properties: {
@@ -257,7 +257,7 @@ async function fetchToolsFromUpstream(client, namespace) {
257
257
 
258
258
  // src/server.ts
259
259
  var POLL_INTERVAL = 6e4;
260
- var ConnectServer = class {
260
+ var ConnectServer = class _ConnectServer {
261
261
  constructor(apiUrl2, token2) {
262
262
  this.apiUrl = apiUrl2;
263
263
  this.token = token2;
@@ -275,6 +275,8 @@ var ConnectServer = class {
275
275
  configVersion = null;
276
276
  pollTimer = null;
277
277
  toolRoutes = /* @__PURE__ */ new Map();
278
+ idleCallCounts = /* @__PURE__ */ new Map();
279
+ static IDLE_CALL_THRESHOLD = 10;
278
280
  setupHandlers() {
279
281
  this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
280
282
  tools: buildToolList(this.connections)
@@ -312,7 +314,12 @@ var ConnectServer = class {
312
314
  if (name === META_TOOLS.deactivate.name) {
313
315
  return this.handleDeactivate(args.server);
314
316
  }
315
- return routeToolCall(name, args, this.toolRoutes, this.connections);
317
+ const route = this.toolRoutes.get(name);
318
+ const result = await routeToolCall(name, args, this.toolRoutes, this.connections);
319
+ if (route) {
320
+ await this.trackUsageAndAutoDeactivate(route.namespace);
321
+ }
322
+ return result;
316
323
  }
317
324
  handleDiscover() {
318
325
  if (!this.config || this.config.servers.length === 0) {
@@ -381,6 +388,7 @@ var ConnectServer = class {
381
388
  try {
382
389
  const connection = await connectToUpstream(serverConfig);
383
390
  this.connections.set(namespace, connection);
391
+ this.idleCallCounts.set(namespace, 0);
384
392
  this.toolRoutes = buildToolRoutes(this.connections);
385
393
  await this.server.sendToolListChanged();
386
394
  const toolNames = connection.tools.map((t) => t.namespacedName).join(", ");
@@ -416,12 +424,40 @@ var ConnectServer = class {
416
424
  }
417
425
  await disconnectFromUpstream(connection);
418
426
  this.connections.delete(namespace);
427
+ this.idleCallCounts.delete(namespace);
419
428
  this.toolRoutes = buildToolRoutes(this.connections);
420
429
  await this.server.sendToolListChanged();
421
430
  return {
422
431
  content: [{ type: "text", text: 'Deactivated "' + namespace + '". Tools removed.' }]
423
432
  };
424
433
  }
434
+ async trackUsageAndAutoDeactivate(calledNamespace) {
435
+ this.idleCallCounts.set(calledNamespace, 0);
436
+ for (const ns of this.connections.keys()) {
437
+ if (ns !== calledNamespace) {
438
+ this.idleCallCounts.set(ns, (this.idleCallCounts.get(ns) ?? 0) + 1);
439
+ }
440
+ }
441
+ const toDeactivate = [];
442
+ for (const [ns, idleCount] of this.idleCallCounts) {
443
+ if (idleCount >= _ConnectServer.IDLE_CALL_THRESHOLD && this.connections.has(ns)) {
444
+ toDeactivate.push(ns);
445
+ }
446
+ }
447
+ for (const ns of toDeactivate) {
448
+ log("info", "Auto-deactivating idle server", { namespace: ns, idleCalls: this.idleCallCounts.get(ns) });
449
+ const connection = this.connections.get(ns);
450
+ if (connection) {
451
+ await disconnectFromUpstream(connection);
452
+ this.connections.delete(ns);
453
+ this.idleCallCounts.delete(ns);
454
+ }
455
+ }
456
+ if (toDeactivate.length > 0) {
457
+ this.toolRoutes = buildToolRoutes(this.connections);
458
+ await this.server.sendToolListChanged();
459
+ }
460
+ }
425
461
  async fetchAndApplyConfig() {
426
462
  const newConfig = await fetchConfig(this.apiUrl, this.token);
427
463
  if (newConfig.configVersion === this.configVersion) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/mcp-connect",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "MCP orchestrator — one install, all your MCP servers, managed from the cloud",
5
5
  "license": "MIT",
6
6
  "author": "Yaw Labs <contact@yaw.sh> (https://yaw.sh)",