ellmos-controlcenter-mcp 0.1.0-alpha.1
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/ARCHITECTURE.md +57 -0
- package/CHANGELOG.md +20 -0
- package/DECISIONS.md +30 -0
- package/LICENSE +22 -0
- package/README.md +180 -0
- package/ROADMAP.md +51 -0
- package/SECURITY.md +34 -0
- package/START.md +39 -0
- package/STATE.md +36 -0
- package/assets/controlcenter-logo.jpg +0 -0
- package/dist/bundles.d.ts +18 -0
- package/dist/bundles.d.ts.map +1 -0
- package/dist/bundles.js +85 -0
- package/dist/bundles.js.map +1 -0
- package/dist/catalog.d.ts +24 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +104 -0
- package/dist/catalog.js.map +1 -0
- package/dist/dashboard.d.ts +4 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +420 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +317 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +12 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +77 -0
- package/dist/policy.js.map +1 -0
- package/dist/profiles.d.ts +57 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +183 -0
- package/dist/profiles.js.map +1 -0
- package/package.json +66 -0
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Architektur
|
|
2
|
+
|
|
3
|
+
## Überblick
|
|
4
|
+
|
|
5
|
+
Der Server ist absichtlich klein gestartet und in zwei Kernmodule geteilt:
|
|
6
|
+
|
|
7
|
+
- `catalog.ts`
|
|
8
|
+
- scannt den lokalen MCP-Root
|
|
9
|
+
- liest `package.json` und optional `server.json`
|
|
10
|
+
- erzeugt strukturierte Server-Zusammenfassungen
|
|
11
|
+
- `profiles.ts`
|
|
12
|
+
- liest Claude-Profile aus `~/.claude/profiles`
|
|
13
|
+
- extrahiert Servernamen und Profilbeziehungen
|
|
14
|
+
- berechnet eine einfache Profilempfehlung per Heuristik
|
|
15
|
+
- löst Profile inklusive `extends` auf
|
|
16
|
+
- schreibt generierte `--mcp-config`-Dateien
|
|
17
|
+
- `bundles.ts`
|
|
18
|
+
- definiert Capability-Bundles
|
|
19
|
+
- gruppiert lokale Server nach Beschreibung, Name und Keywords
|
|
20
|
+
- empfiehlt Bundles anhand von Aufgaben-Keywords
|
|
21
|
+
- `policy.ts`
|
|
22
|
+
- auditiert aufgelöste Profile
|
|
23
|
+
- meldet erste Risiken wie `npx`-Starts, Env-Secrets und ungültige Server-Konfigurationen
|
|
24
|
+
- gibt keine Secret-Werte aus
|
|
25
|
+
- `dashboard.ts`
|
|
26
|
+
- stellt eine lokale Browser-GUI bereit
|
|
27
|
+
- nutzt dieselbe Catalog-, Profile-, Bundle- und Policy-Logik
|
|
28
|
+
- schreibt Profile nur nach explizitem Server-Toggle
|
|
29
|
+
|
|
30
|
+
`index.ts` bildet darauf die MCP-Tools ab und kümmert sich um Formatierung und Ausgaben.
|
|
31
|
+
|
|
32
|
+
## Geplante Zielarchitektur
|
|
33
|
+
|
|
34
|
+
### Phase 1: Sichtbarkeit
|
|
35
|
+
|
|
36
|
+
- Server-Katalog
|
|
37
|
+
- Profilübersicht
|
|
38
|
+
- Profilempfehlung
|
|
39
|
+
|
|
40
|
+
### Phase 2: Steuerung
|
|
41
|
+
|
|
42
|
+
- Profilwechsel
|
|
43
|
+
- Profil-Templates
|
|
44
|
+
- Tool-Bundles
|
|
45
|
+
|
|
46
|
+
### Phase 3: Governance
|
|
47
|
+
|
|
48
|
+
- Policy-Layer
|
|
49
|
+
- Rechte und Freigaben
|
|
50
|
+
- Audit und Trace
|
|
51
|
+
|
|
52
|
+
### Phase 4: Orchestrierung
|
|
53
|
+
|
|
54
|
+
- Langläufer
|
|
55
|
+
- Checkpoints
|
|
56
|
+
- Human Approval
|
|
57
|
+
- Resume / Retry
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0-alpha.1 - 2026-05-23
|
|
4
|
+
|
|
5
|
+
- Alpha-Release für GitHub und npm vorbereitet
|
|
6
|
+
- Dashboard-Schreibaktionen mit Bestätigung und Backups abgesichert
|
|
7
|
+
- Security-Dokumentation ergänzt
|
|
8
|
+
- README mit Alpha-Hinweis, npm-Installation und Grenzen aktualisiert
|
|
9
|
+
|
|
10
|
+
## 0.1.0 - 2026-05-23
|
|
11
|
+
|
|
12
|
+
- Neues MVP-Repo für `ellmos-controlcenter-mcp` angelegt
|
|
13
|
+
- Discovery- und Profil-Grundfunktionen vorbereitet
|
|
14
|
+
- Build-, Test- und Registry-Basisdateien ergänzt
|
|
15
|
+
- Profilauflösung und `controlcenter_switch_profile` ergänzt
|
|
16
|
+
- Capability-Bundles und Bundle-Empfehlungen ergänzt
|
|
17
|
+
- Erstes Profil-Audit mit Policy-Findings ergänzt
|
|
18
|
+
- `ROADMAP.md` und lokales Browser-Dashboard ergänzt
|
|
19
|
+
- GitHub-CI und Emblem-Asset ergänzt
|
|
20
|
+
- Logo auf bereitgestellte ControlCenter-JPG-Datei umgestellt
|
package/DECISIONS.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Entscheidungen
|
|
2
|
+
|
|
3
|
+
## Name
|
|
4
|
+
|
|
5
|
+
**Gewählt:** `ellmos-controlcenter-mcp`
|
|
6
|
+
|
|
7
|
+
Begründung:
|
|
8
|
+
|
|
9
|
+
- klingt nach Steuerzentrale statt Werkzeugablage
|
|
10
|
+
- bleibt offen für spätere Profile-, Policy-, Gateway- und Registry-Funktionen
|
|
11
|
+
- ist klarer und professioneller als ein Sammelbegriff wie `toolcollectorhub`
|
|
12
|
+
|
|
13
|
+
## Scope des MVP
|
|
14
|
+
|
|
15
|
+
Der MVP startet bewusst **nicht** mit:
|
|
16
|
+
|
|
17
|
+
- OAuth
|
|
18
|
+
- Gateway-Regeln
|
|
19
|
+
- Profilwechsel in Live-Configs
|
|
20
|
+
- Tool-Ausführung auf fremden Servern
|
|
21
|
+
|
|
22
|
+
Stattdessen beginnt der Server mit:
|
|
23
|
+
|
|
24
|
+
- lokaler Sichtbarkeit
|
|
25
|
+
- Katalogbau
|
|
26
|
+
- Profilanalyse
|
|
27
|
+
- Empfehlung
|
|
28
|
+
|
|
29
|
+
So entsteht zuerst ein belastbarer Kern, statt viele große Rollen nur anzudeuten.
|
|
30
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lukas
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# ellmos ControlCenter MCP
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/controlcenter-logo.jpg" alt="ellmos ControlCenter MCP logo" width="420">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
> Ein MCP-Server als Steuerzentrale für den lokalen MCP-Stack: Server entdecken, Profile lesen, passende Profile empfehlen und Kataloge erzeugen.
|
|
8
|
+
|
|
9
|
+
*Part of the [ellmos-ai](https://github.com/ellmos-ai) family.*
|
|
10
|
+
|
|
11
|
+
`ellmos-controlcenter-mcp` ist als Infrastruktur-Schicht über euren bestehenden MCP-Servern gedacht. Der erste Alpha-Release konzentriert sich bewusst auf **Discovery, Profilsicht, Dashboard, Capability-Bundles und erste Policy-Audits**. Gateway, echtes Policy-Enforcement, Auth und Tool-Level-Rechte sind geplant, aber noch nicht als Sicherheitsgrenze umgesetzt.
|
|
12
|
+
|
|
13
|
+
> **Alpha-Hinweis:** Diese Version ist nützlich für lokale Verwaltung und Preview-Tests. Sie ist noch kein abgesicherter MCP-Gateway und sollte nicht als Schutzschicht für untrusted Tools oder fremde Nutzer verwendet werden.
|
|
14
|
+
|
|
15
|
+
## Status
|
|
16
|
+
|
|
17
|
+
- **Phase:** Alpha
|
|
18
|
+
- **Version:** `0.1.0-alpha.1`
|
|
19
|
+
- **Repository:** `ellmos-ai/ellmos-controlcenter-mcp`
|
|
20
|
+
- **CI:** `npm run test` and `npm run build`
|
|
21
|
+
- **Ziel:** Lokalen MCP-Stack sichtbar und steuerbar machen
|
|
22
|
+
- **Schwerpunkt:** Katalogisierung, Profilübersicht, Profilempfehlung
|
|
23
|
+
|
|
24
|
+
## Erste Tools im MVP
|
|
25
|
+
|
|
26
|
+
| Tool | Zweck |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `controlcenter_status` | Überblick über Stack, Profile und erkannte Server |
|
|
29
|
+
| `controlcenter_list_local_servers` | Lokale MCP-Repos im MCP-Root scannen |
|
|
30
|
+
| `controlcenter_list_bundles` | Lokale Server nach Capability-Bundles gruppieren |
|
|
31
|
+
| `controlcenter_suggest_bundles` | Passende Bundles für eine Aufgabe empfehlen |
|
|
32
|
+
| `controlcenter_list_profiles` | Claude-Profile aus `~/.claude/profiles` auflisten |
|
|
33
|
+
| `controlcenter_suggest_profile` | Zu einer Aufgabe ein passendes Profil empfehlen |
|
|
34
|
+
| `controlcenter_resolve_profile` | Ein Profil inklusive `extends` auflösen |
|
|
35
|
+
| `controlcenter_switch_profile` | Eine startbare `--mcp-config`-Datei vorbereiten |
|
|
36
|
+
| `controlcenter_audit_profile` | Ein Profil auf erste Policy-Hinweise prüfen |
|
|
37
|
+
| `controlcenter_build_catalog` | Einen JSON-Katalog der lokalen Server erzeugen |
|
|
38
|
+
|
|
39
|
+
## Browser-GUI
|
|
40
|
+
|
|
41
|
+
Das lokale Dashboard startet nach dem Build mit:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run dashboard
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Standardadresse:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
http://127.0.0.1:3737
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Die GUI kann aktuell lokale Server anzeigen, Profile anzeigen, Server pro Profil aktivieren/deaktivieren, Profil-Audits zusammenfassen und eine generierte `--mcp-config` schreiben. Schreibaktionen verlangen eine Bestätigung und legen vor dem Überschreiben ein Backup an.
|
|
54
|
+
|
|
55
|
+
## ellmos Ecosystem Entry
|
|
56
|
+
|
|
57
|
+
**ellmos ControlCenter MCP** verwaltet lokale MCP-Server, Claude-Profile, Capability-Bundles und erste Policy-Audits. Es ist der geplante Control-Plane- und Gateway-Baustein für Tool-Bloat-Management, Profilwechsel und spätere Tool-Level-Rechte im ellmos-Ökosystem.
|
|
58
|
+
|
|
59
|
+
## Aktuelle Grenzen
|
|
60
|
+
|
|
61
|
+
- Tool-Level-Rechte sind sichtbar geplant, aber noch nicht technisch durchgesetzt.
|
|
62
|
+
- Ein aktivierter Backend-MCP-Server stellt weiterhin alle eigenen Tools bereit, solange kein Gateway davor geschaltet ist.
|
|
63
|
+
- Das Dashboard ist ein lokales Admin-Werkzeug ohne Benutzerlogin.
|
|
64
|
+
- Die i18n-Schicht ist noch nicht implementiert; die Alpha-Oberfläche ist deutschsprachig.
|
|
65
|
+
|
|
66
|
+
## Installation via npm
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install -g ellmos-controlcenter-mcp@alpha
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
MCP-Server starten:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
ellmos-controlcenter
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Dashboard starten:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
ellmos-controlcenter-dashboard
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd C:\Users\User\OneDrive\.TOPICS\.AI\.MCP\ellmos-controlcenter-mcp
|
|
88
|
+
npm install
|
|
89
|
+
npm run build
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd C:\Users\User\OneDrive\.TOPICS\.AI\.MCP\ellmos-controlcenter-mcp
|
|
96
|
+
npm run build
|
|
97
|
+
node dist/index.js
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Konfiguration
|
|
101
|
+
|
|
102
|
+
### Claude Desktop / Claude Code
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"mcpServers": {
|
|
107
|
+
"controlcenter": {
|
|
108
|
+
"command": "node",
|
|
109
|
+
"args": [
|
|
110
|
+
"C:/Users/User/OneDrive/.TOPICS/.AI/.MCP/ellmos-controlcenter-mcp/dist/index.js"
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Optional:
|
|
118
|
+
|
|
119
|
+
- `ELLMOS_MCP_ROOT` überschreibt den Standard-MCP-Root
|
|
120
|
+
- `ELLMOS_PROFILE_ROOT` überschreibt den Claude-Profilordner
|
|
121
|
+
|
|
122
|
+
## Profilwechsel
|
|
123
|
+
|
|
124
|
+
`controlcenter_switch_profile` verändert keine laufende Claude-Session. Das Tool erzeugt eine aufgelöste MCP-Konfiguration und gibt den passenden Startbefehl zurück:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
claude --mcp-config C:\Users\User\.claude\profiles\_generated\software.mcp.json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Mit `write: false` läuft der Wechsel als Vorschau. Mit `write: true` wird die generierte Datei geschrieben.
|
|
131
|
+
|
|
132
|
+
## Capability-Bundles
|
|
133
|
+
|
|
134
|
+
ControlCenter gruppiert lokale Server aktuell in diese Bundles:
|
|
135
|
+
|
|
136
|
+
- `core-local`
|
|
137
|
+
- `software`
|
|
138
|
+
- `filesystem`
|
|
139
|
+
- `automation`
|
|
140
|
+
- `control-plane`
|
|
141
|
+
|
|
142
|
+
Das ist die Grundlage für späteres Tool-Bloat-Management: statt viele Einzeltools direkt sichtbar zu machen, kann ein Agent zuerst ein passendes Aufgaben-Bundle wählen.
|
|
143
|
+
|
|
144
|
+
## Profil-Audit
|
|
145
|
+
|
|
146
|
+
`controlcenter_audit_profile` ist die erste kleine Policy-Schicht. Sie markiert aktuell:
|
|
147
|
+
|
|
148
|
+
- `npx`-Starts
|
|
149
|
+
- Environment-Variablen in Server-Konfigurationen
|
|
150
|
+
- fehlende oder ungültige Server-Kommandos
|
|
151
|
+
- sensitive Namensbestandteile in Args
|
|
152
|
+
|
|
153
|
+
Environment-Werte werden dabei nicht ausgegeben.
|
|
154
|
+
|
|
155
|
+
## Projekt-Struktur
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
ellmos-controlcenter-mcp/
|
|
159
|
+
├── src/
|
|
160
|
+
├── test/
|
|
161
|
+
├── data/
|
|
162
|
+
├── README.md
|
|
163
|
+
├── START.md
|
|
164
|
+
├── ARCHITECTURE.md
|
|
165
|
+
├── STATE.md
|
|
166
|
+
├── DECISIONS.md
|
|
167
|
+
└── TODO.md
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Dokumentation
|
|
171
|
+
|
|
172
|
+
| Für... | lies... |
|
|
173
|
+
|---|---|
|
|
174
|
+
| Schnellstart | [START.md](./START.md) |
|
|
175
|
+
| Aktuellen Stand | [STATE.md](./STATE.md) |
|
|
176
|
+
| Architektur | [ARCHITECTURE.md](./ARCHITECTURE.md) |
|
|
177
|
+
| Roadmap | [ROADMAP.md](./ROADMAP.md) |
|
|
178
|
+
| Entscheidungen | [DECISIONS.md](./DECISIONS.md) |
|
|
179
|
+
| Offene Aufgaben | [TODO.md](./TODO.md) |
|
|
180
|
+
| Änderungen | [CHANGELOG.md](./CHANGELOG.md) |
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
## Zielbild
|
|
4
|
+
|
|
5
|
+
`ellmos-controlcenter-mcp` soll zur lokalen Steuerzentrale für MCP-Server werden. Langfristig verwaltet es Profile, Server, Tools, Rechte, Audits und einen virtuellen Gateway-MCP, über den ausgewählte Tools als ein kontrollierter Server bereitgestellt werden.
|
|
6
|
+
|
|
7
|
+
## Phase 1: ControlCenter Dashboard
|
|
8
|
+
|
|
9
|
+
Status: begonnen
|
|
10
|
+
|
|
11
|
+
- Lokale MCP-Server anzeigen
|
|
12
|
+
- Claude-Profile anzeigen
|
|
13
|
+
- Server pro Profil aktivieren und deaktivieren
|
|
14
|
+
- Audit-Hinweise sichtbar machen
|
|
15
|
+
- Generierte `--mcp-config`-Dateien schreiben
|
|
16
|
+
|
|
17
|
+
## Phase 2: Tool-Katalog
|
|
18
|
+
|
|
19
|
+
Status: geplant
|
|
20
|
+
|
|
21
|
+
- Backend-MCP-Server starten
|
|
22
|
+
- `list_tools` pro Server auslesen
|
|
23
|
+
- Toolnamen, Beschreibungen und Schemas speichern
|
|
24
|
+
- Tools Capability-Bundles zuordnen
|
|
25
|
+
|
|
26
|
+
## Phase 3: Rechte und Policies
|
|
27
|
+
|
|
28
|
+
Status: geplant
|
|
29
|
+
|
|
30
|
+
- `policy.json` für Profile, Server und Tools
|
|
31
|
+
- Rechte: `allow`, `deny`, `ask`, `readonly`
|
|
32
|
+
- Secret-Werte maskieren
|
|
33
|
+
- Audit-Log für Profil- und Rechteänderungen
|
|
34
|
+
|
|
35
|
+
## Phase 4: Virtueller MCP-Gateway
|
|
36
|
+
|
|
37
|
+
Status: geplant
|
|
38
|
+
|
|
39
|
+
- Claude lädt nur noch den virtuellen `ellmos-controlcenter-gateway`
|
|
40
|
+
- Gateway startet ausgewählte Backend-Server
|
|
41
|
+
- Gateway veröffentlicht nur erlaubte Tools
|
|
42
|
+
- Tool-Aufrufe werden geprüft, geloggt und weitergeleitet
|
|
43
|
+
|
|
44
|
+
## Phase 5: Veröffentlichung
|
|
45
|
+
|
|
46
|
+
Status: geplant
|
|
47
|
+
|
|
48
|
+
- i18n mit Deutsch und Englisch
|
|
49
|
+
- stabiler Katalogstandard
|
|
50
|
+
- Sicherheitsdokumentation
|
|
51
|
+
- Paketierung für npm und GitHub
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
## Alpha Status
|
|
4
|
+
|
|
5
|
+
`ellmos-controlcenter-mcp` is currently an alpha release. It can inspect profiles, prepare generated MCP configs, show local MCP servers, and audit first policy hints. It is not yet a security boundary.
|
|
6
|
+
|
|
7
|
+
## Current Safety Model
|
|
8
|
+
|
|
9
|
+
- Dashboard write actions require explicit confirmation.
|
|
10
|
+
- Existing profile files and generated MCP config files are backed up before overwrite.
|
|
11
|
+
- Environment values are not printed by the profile audit.
|
|
12
|
+
- The dashboard binds to `127.0.0.1` by default.
|
|
13
|
+
|
|
14
|
+
## Not Yet Implemented
|
|
15
|
+
|
|
16
|
+
- Tool-level policy enforcement
|
|
17
|
+
- MCP gateway/proxy enforcement
|
|
18
|
+
- User authentication for the dashboard
|
|
19
|
+
- Remote multi-user access control
|
|
20
|
+
- Tamper-proof audit logs
|
|
21
|
+
|
|
22
|
+
## Recommendations
|
|
23
|
+
|
|
24
|
+
- Run the dashboard only on trusted local machines.
|
|
25
|
+
- Keep Claude profiles small and task-specific.
|
|
26
|
+
- Do not expose the dashboard host publicly.
|
|
27
|
+
- Treat `npx`-based MCP servers as less reproducible than pinned local paths.
|
|
28
|
+
- Review generated MCP configs before launching a client with them.
|
|
29
|
+
|
|
30
|
+
## Reporting
|
|
31
|
+
|
|
32
|
+
For private preview issues, use the private GitHub repository:
|
|
33
|
+
|
|
34
|
+
https://github.com/ellmos-ai/ellmos-controlcenter-mcp/issues
|
package/START.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Start
|
|
2
|
+
|
|
3
|
+
## Zweck
|
|
4
|
+
|
|
5
|
+
`ellmos-controlcenter-mcp` soll der zentrale Einstiegspunkt für den lokalen MCP-Bestand werden:
|
|
6
|
+
|
|
7
|
+
- Welche MCP-Server liegen lokal vor?
|
|
8
|
+
- Welche Claude-Profile gibt es?
|
|
9
|
+
- Welches Profil passt zu einer Aufgabe?
|
|
10
|
+
- Wie kann daraus später ein echter Control-Plane- oder Gateway-Server werden?
|
|
11
|
+
|
|
12
|
+
## Lokaler Schnelltest
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cd C:\Users\User\OneDrive\.TOPICS\.AI\.MCP\ellmos-controlcenter-mcp
|
|
16
|
+
npm install
|
|
17
|
+
npm run test
|
|
18
|
+
npm run build
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Nächster Ausbauschritt
|
|
22
|
+
|
|
23
|
+
Nach dem MVP sollten diese Schichten ergänzt werden:
|
|
24
|
+
|
|
25
|
+
1. Profilwechsel und Profilschreiben
|
|
26
|
+
2. Tool-Gruppen und Capability-Bundles
|
|
27
|
+
3. Policy-Regeln und Allow-/Deny-Logik
|
|
28
|
+
4. Job-State für längere Abläufe
|
|
29
|
+
5. Optional: Registry- und Gateway-Modus
|
|
30
|
+
|
|
31
|
+
## Profilwechsel vorbereiten
|
|
32
|
+
|
|
33
|
+
Der MVP kann Profile auflösen und eine startbare MCP-Config schreiben:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
claude --mcp-config C:\Users\User\.claude\profiles\_generated\software.mcp.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Der Wechsel wird dadurch explizit und nachvollziehbar. Eine laufende Claude-Session wird nicht automatisch beendet.
|
package/STATE.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# State
|
|
2
|
+
|
|
3
|
+
## Aktueller Stand
|
|
4
|
+
|
|
5
|
+
- Repo angelegt
|
|
6
|
+
- TypeScript-MCP-Grundgerüst vorhanden
|
|
7
|
+
- Erste Discovery- und Profiltools implementiert
|
|
8
|
+
- Tests für Kernlogik vorhanden
|
|
9
|
+
- Version `0.1.0-alpha.1` für GitHub/npm-Release vorbereitet
|
|
10
|
+
|
|
11
|
+
## Was der MVP schon kann
|
|
12
|
+
|
|
13
|
+
- Lokale MCP-Repos erkennen
|
|
14
|
+
- Metadaten aus `package.json` lesen
|
|
15
|
+
- Tool-Anzahlen grob aus Beschreibungen ableiten
|
|
16
|
+
- Claude-Profile lesen und zusammenfassen
|
|
17
|
+
- Ein Profil anhand von Aufgaben-Keywords empfehlen
|
|
18
|
+
- Einen JSON-Katalog schreiben
|
|
19
|
+
- Profile inklusive `extends` auflösen
|
|
20
|
+
- Generierte `--mcp-config`-Dateien für Profilwechsel vorbereiten
|
|
21
|
+
- Lokale Server in Capability-Bundles gruppieren
|
|
22
|
+
- Capability-Bundles anhand einer Aufgabe empfehlen
|
|
23
|
+
- Profile auf erste Policy-Hinweise auditieren
|
|
24
|
+
- Lokales Browser-Dashboard starten
|
|
25
|
+
- Server pro Profil über das Dashboard aktivieren/deaktivieren
|
|
26
|
+
- Schreibaktionen mit Bestätigung und Backup absichern
|
|
27
|
+
|
|
28
|
+
## Was noch fehlt
|
|
29
|
+
|
|
30
|
+
- Tool-Level-Erkennung per echter MCP-`list_tools`-Abfrage
|
|
31
|
+
- Tool-Level-Aktivierung über Gateway
|
|
32
|
+
- Tool-Gruppen dauerhaft konfigurieren
|
|
33
|
+
- Durchsetzende Policy-/Gateway-Schicht
|
|
34
|
+
- Remote-Registry-Unterstützung
|
|
35
|
+
- Observability mit Traces und Replay
|
|
36
|
+
- Laufende Claude-Sessions automatisch neu starten
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { LocalServerSummary } from "./catalog.js";
|
|
2
|
+
export interface CapabilityBundle {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
keywords: string[];
|
|
7
|
+
serverCount: number;
|
|
8
|
+
totalTools: number | null;
|
|
9
|
+
servers: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface BundleSuggestion {
|
|
12
|
+
bundle: CapabilityBundle;
|
|
13
|
+
score: number;
|
|
14
|
+
matchedKeywords: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function buildCapabilityBundles(servers: LocalServerSummary[]): CapabilityBundle[];
|
|
17
|
+
export declare function suggestCapabilityBundles(task: string, bundles: CapabilityBundle[]): BundleSuggestion[];
|
|
18
|
+
//# sourceMappingURL=bundles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundles.d.ts","sourceRoot":"","sources":["../src/bundles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAkED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,gBAAgB,EAAE,CAkBxF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAatG"}
|
package/dist/bundles.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const BUNDLE_DEFINITIONS = [
|
|
2
|
+
{
|
|
3
|
+
id: "core-local",
|
|
4
|
+
title: "Core Local Stack",
|
|
5
|
+
description: "Lokale ellmos-Basistools für Dateien, Code, Automatisierung und Steuerung.",
|
|
6
|
+
keywords: ["ellmos", "bach", "local", "control", "commander", "mcp"]
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "software",
|
|
10
|
+
title: "Software Development",
|
|
11
|
+
description: "Codeanalyse, Build-Hilfen, Developer Tools und Repository-Arbeit.",
|
|
12
|
+
keywords: ["code", "developer", "typescript", "python", "analysis", "json", "diff", "regex", "build"]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "filesystem",
|
|
16
|
+
title: "Filesystem Operations",
|
|
17
|
+
description: "Dateiverwaltung, Suche, Prozesse, Archive und lokale Arbeitsordner.",
|
|
18
|
+
keywords: ["file", "filesystem", "directory", "process", "zip", "archive", "ocr", "markdown"]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "automation",
|
|
22
|
+
title: "Automation",
|
|
23
|
+
description: "n8n, Workflows, Automatisierung und wiederkehrende Abläufe.",
|
|
24
|
+
keywords: ["n8n", "automation", "workflow", "workflows", "schedule", "trigger"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "control-plane",
|
|
28
|
+
title: "Control Plane",
|
|
29
|
+
description: "Profilverwaltung, Kataloge, Server-Discovery und spätere Policy-Schicht.",
|
|
30
|
+
keywords: ["control", "profile", "catalog", "registry", "policy", "gateway", "discovery"]
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
function buildHaystack(server) {
|
|
34
|
+
return [
|
|
35
|
+
server.directoryName,
|
|
36
|
+
server.packageName,
|
|
37
|
+
server.mcpName ?? "",
|
|
38
|
+
server.description,
|
|
39
|
+
server.keywords.join(" ")
|
|
40
|
+
].join(" ").toLowerCase();
|
|
41
|
+
}
|
|
42
|
+
function tokenize(value) {
|
|
43
|
+
return new Set(value.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean));
|
|
44
|
+
}
|
|
45
|
+
function matchesKeyword(haystackTokens, keyword) {
|
|
46
|
+
const keywordTokens = keyword.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
47
|
+
return keywordTokens.length > 0 && keywordTokens.every((token) => haystackTokens.has(token));
|
|
48
|
+
}
|
|
49
|
+
function matchesDefinition(server, definition) {
|
|
50
|
+
const haystackTokens = tokenize(buildHaystack(server));
|
|
51
|
+
return definition.keywords.some((keyword) => matchesKeyword(haystackTokens, keyword));
|
|
52
|
+
}
|
|
53
|
+
export function buildCapabilityBundles(servers) {
|
|
54
|
+
return BUNDLE_DEFINITIONS.map((definition) => {
|
|
55
|
+
const matchedServers = servers
|
|
56
|
+
.filter((server) => matchesDefinition(server, definition))
|
|
57
|
+
.map((server) => server.packageName)
|
|
58
|
+
.sort((a, b) => a.localeCompare(b));
|
|
59
|
+
const toolCounts = servers
|
|
60
|
+
.filter((server) => matchedServers.includes(server.packageName))
|
|
61
|
+
.map((server) => server.toolCount)
|
|
62
|
+
.filter((count) => typeof count === "number");
|
|
63
|
+
return {
|
|
64
|
+
...definition,
|
|
65
|
+
serverCount: matchedServers.length,
|
|
66
|
+
totalTools: toolCounts.length > 0 ? toolCounts.reduce((sum, count) => sum + count, 0) : null,
|
|
67
|
+
servers: matchedServers
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
export function suggestCapabilityBundles(task, bundles) {
|
|
72
|
+
const taskTokens = tokenize(task);
|
|
73
|
+
return bundles
|
|
74
|
+
.map((bundle) => {
|
|
75
|
+
const matchedKeywords = bundle.keywords.filter((keyword) => matchesKeyword(taskTokens, keyword));
|
|
76
|
+
return {
|
|
77
|
+
bundle,
|
|
78
|
+
score: matchedKeywords.length,
|
|
79
|
+
matchedKeywords
|
|
80
|
+
};
|
|
81
|
+
})
|
|
82
|
+
.filter((suggestion) => suggestion.score > 0)
|
|
83
|
+
.sort((a, b) => b.score - a.score || a.bundle.id.localeCompare(b.bundle.id));
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=bundles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundles.js","sourceRoot":"","sources":["../src/bundles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const DEFAULT_MCP_ROOT: string;
|
|
2
|
+
export interface LocalServerSummary {
|
|
3
|
+
directoryName: string;
|
|
4
|
+
packageName: string;
|
|
5
|
+
mcpName: string | null;
|
|
6
|
+
version: string | null;
|
|
7
|
+
description: string;
|
|
8
|
+
absolutePath: string;
|
|
9
|
+
binName: string | null;
|
|
10
|
+
entryPoint: string | null;
|
|
11
|
+
toolCount: number | null;
|
|
12
|
+
hasServerJson: boolean;
|
|
13
|
+
keywords: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface McpServerConfig {
|
|
16
|
+
command: string;
|
|
17
|
+
args: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function extractToolCountFromDescription(description: string | undefined): number | null;
|
|
20
|
+
export declare function discoverLocalServerDirectories(mcpRoot?: string): Promise<string[]>;
|
|
21
|
+
export declare function readLocalServerSummary(serverDirectory: string, mcpRoot?: string): Promise<LocalServerSummary | null>;
|
|
22
|
+
export declare function scanLocalServers(mcpRoot?: string): Promise<LocalServerSummary[]>;
|
|
23
|
+
export declare function createLocalServerMcpConfig(server: LocalServerSummary): McpServerConfig;
|
|
24
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,QACmD,CAAC;AAWjF,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAgBD,wBAAgB,+BAA+B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAK9F;AAWD,wBAAsB,8BAA8B,CAAC,OAAO,GAAE,MAAyB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ1G;AAED,wBAAsB,sBAAsB,CAAC,eAAe,EAAE,MAAM,EAAE,OAAO,GAAE,MAAyB,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAsD5I;AAED,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,MAAyB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAIxG;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,eAAe,CAMtF"}
|