@yawlabs/mcp-connect 0.1.1 → 0.1.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 +174 -0
- package/dist/index.js +41 -5
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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