Skip to content

Commit 7dc945c

Browse files
committed
Add enterprise token management guide
Companies with dozens of engineers end up managing hundreds of scattered API tokens across laptops and AI tools, with no central audit or easy rotation. Wrote a guide explaining the problem and showing how the proxy solves it — one central server holds service tokens, devs connect with a single endpoint, ACL controls access per user. Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
1 parent fb59e4d commit 7dc945c

3 files changed

Lines changed: 250 additions & 15 deletions

File tree

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* [Registry](guides/registry.md)
1616
* [Scripting](guides/scripting.md)
1717
* [Proxy mode](guides/proxy-mode.md)
18+
* [Enterprise token management](guides/enterprise-token-management.md)
1819

1920
## How-to
2021

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# Token sprawl in enterprise teams
2+
3+
## The problem
4+
5+
A company with 50 engineers using AI tools (Claude Code, Cursor, Windsurf) needs access to Sentry, Slack, GitHub, Grafana, and a few internal services. Each service requires an API token. Each engineer needs their own tokens.
6+
7+
The math gets ugly fast:
8+
9+
| Engineers | Services | Tokens to manage |
10+
|-----------|----------|-----------------|
11+
| 10 | 5 | 50 |
12+
| 50 | 8 | 400 |
13+
| 200 | 12 | 2,400 |
14+
15+
Every token is a secret. Every secret is a liability. Every engineer's laptop is an attack surface.
16+
17+
Now multiply by the number of AI tools each engineer uses. Claude Code needs a `servers.json`. Cursor needs another. Windsurf needs a third. Same tokens, copied across machines, across config files, across tools.
18+
19+
**What actually goes wrong:**
20+
21+
- **Onboarding takes hours** — new engineer joins, needs tokens for 8 services. Opens 8 dashboards, generates 8 tokens, pastes them into 3 config files. One typo somewhere. Debug time.
22+
- **Offboarding is incomplete** — engineer leaves, someone revokes their GitHub token but forgets the Sentry one. The Grafana token lives on a laptop image that gets recycled.
23+
- **Token rotation is a myth** — security policy says rotate every 90 days. Nobody does it because it means updating tokens across every developer machine, every config file, every tool.
24+
- **Audit is impossible** — "who has access to what?" requires checking every engineer's local config. There's no central log. No visibility.
25+
- **Scope creep** — engineers generate broad-access tokens because it's easier than figuring out the minimum permissions. One leaked token exposes everything.
26+
27+
## The fix: one proxy, zero tokens on developer machines
28+
29+
Instead of distributing tokens to every engineer, run one MCP proxy on internal infrastructure. The proxy holds the service tokens. Engineers connect to the proxy.
30+
31+
**Before** — every dev holds tokens for every service:
32+
33+
```mermaid
34+
graph LR
35+
D1[Dev 1] -->|token| Sentry
36+
D1 -->|token| Slack
37+
D1 -->|token| GitHub
38+
D2[Dev 2] -->|token| Sentry
39+
D2 -->|token| Slack
40+
D2 -->|token| GitHub
41+
D3[Dev 3] -->|token| Sentry
42+
D3 -->|token| Slack
43+
D3 -->|token| GitHub
44+
45+
style Sentry fill:#f96
46+
style Slack fill:#f96
47+
style GitHub fill:#f96
48+
```
49+
50+
> 50 devs × 8 services = **400 tokens** scattered across laptops.
51+
52+
**After** — one proxy holds the tokens, devs connect once:
53+
54+
```mermaid
55+
graph LR
56+
D1[Dev 1] -->|auth| Proxy
57+
D2[Dev 2] -->|auth| Proxy
58+
D3[Dev 3] -->|auth| Proxy
59+
60+
Proxy["mcp serve<br/>(proxy)"] -->|token| Sentry
61+
Proxy -->|token| Slack
62+
Proxy -->|token| GitHub
63+
64+
style Proxy fill:#4a9,color:#fff
65+
style Sentry fill:#f96
66+
style Slack fill:#f96
67+
style GitHub fill:#f96
68+
```
69+
70+
> 50 devs × 1 connection = **50 auth credentials**, centrally managed.
71+
72+
Service tokens live in one place. If you need to rotate the Sentry token, you update it once on the proxy. Zero touch on developer machines.
73+
74+
## How to set it up
75+
76+
### 1. Deploy the proxy
77+
78+
On a shared server (VM, container, Kubernetes pod):
79+
80+
```bash
81+
mcp serve --http 0.0.0.0:8080 --insecure
82+
```
83+
84+
> In production, put a reverse proxy (nginx, Caddy) in front for TLS. See the [proxy mode guide](proxy-mode.md#production-deployment) for details.
85+
86+
### 2. Configure backend tokens on the proxy
87+
88+
The proxy's `servers.json` holds all service tokens:
89+
90+
```json
91+
{
92+
"mcpServers": {
93+
"sentry": {
94+
"url": "https://mcp.sentry.dev/sse",
95+
"headers": { "Authorization": "Bearer ${SENTRY_TOKEN}" }
96+
},
97+
"slack": {
98+
"command": "npx",
99+
"args": ["-y", "@anthropic-ai/mcp-server-slack"],
100+
"env": { "SLACK_BOT_TOKEN": "${SLACK_TOKEN}" }
101+
},
102+
"github": {
103+
"command": "npx",
104+
"args": ["-y", "@modelcontextprotocol/server-github"],
105+
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }
106+
}
107+
}
108+
}
109+
```
110+
111+
All secrets stay on the server. Engineers never see them.
112+
113+
### 3. Add per-user authentication
114+
115+
Use bearer tokens to identify each engineer and control access:
116+
117+
```json
118+
{
119+
"mcpServers": { ... },
120+
"serverAuth": {
121+
"provider": "bearer",
122+
"bearer": {
123+
"tokens": {
124+
"eng-alice-a1b2c3": "alice",
125+
"eng-bob-d4e5f6": "bob",
126+
"eng-carol-g7h8i9": "carol"
127+
}
128+
},
129+
"acl": {
130+
"default": "allow",
131+
"rules": [
132+
{ "subjects": ["carol"], "tools": ["sentry__*"], "policy": "deny" }
133+
]
134+
}
135+
}
136+
}
137+
```
138+
139+
Or, if you already have an identity provider behind a reverse proxy:
140+
141+
```json
142+
{
143+
"serverAuth": {
144+
"provider": "forwarded",
145+
"forwarded": { "header": "x-forwarded-user" }
146+
}
147+
}
148+
```
149+
150+
See the [proxy mode authentication docs](proxy-mode.md#authentication) for all provider options.
151+
152+
### 4. Engineers connect — one line
153+
154+
Each engineer adds one entry to their local config:
155+
156+
```bash
157+
mcp add --url https://mcp.internal:8443/mcp team
158+
```
159+
160+
That's it. Every AI tool on their machine connects through this one endpoint. No service tokens on their laptop. No per-service config.
161+
162+
```json
163+
{
164+
"mcpServers": {
165+
"team": {
166+
"url": "https://mcp.internal:8443/mcp",
167+
"headers": { "Authorization": "Bearer eng-alice-a1b2c3" }
168+
}
169+
}
170+
}
171+
```
172+
173+
## What you gain
174+
175+
**Onboarding in minutes** — new engineer gets one proxy token. Immediately has access to all approved tools. No 8-service token generation dance.
176+
177+
**Offboarding in seconds** — remove the engineer's token from the proxy config. Access to everything is revoked instantly. No forgotten tokens on decommissioned laptops.
178+
179+
**Token rotation without pain** — rotate a service token on the proxy, no engineer even notices. Zero coordination, zero downtime.
180+
181+
**Audit in one place** — proxy logs show who called which tool, when. One log stream, one place to look.
182+
183+
**Least privilege by default** — ACL rules control which engineers can use which tools. Carol from marketing can use Slack tools but not Sentry admin tools.
184+
185+
**Tool-agnostic** — engineers can use Claude Code, Cursor, Windsurf, or any MCP-compatible client. All connect to the same proxy. Add or remove AI tools without touching service credentials.
186+
187+
## Architecture
188+
189+
```mermaid
190+
graph TB
191+
subgraph Developers["Developer machines (no service tokens)"]
192+
D1["Dev 1<br/>Claude Code"]
193+
D2["Dev 2<br/>Cursor"]
194+
D3["Dev 3<br/>Windsurf"]
195+
end
196+
197+
subgraph Infra["Internal infrastructure"]
198+
LB["nginx / Caddy<br/>(TLS termination)"]
199+
Proxy["mcp serve --http<br/>(proxy + auth + ACL)"]
200+
201+
subgraph Backends["Backend services (tokens stored here)"]
202+
Sentry
203+
Slack
204+
GitHub
205+
Grafana
206+
end
207+
end
208+
209+
D1 -->|"Bearer token<br/>(HTTPS)"| LB
210+
D2 -->|"Bearer token<br/>(HTTPS)"| LB
211+
D3 -->|"Bearer token<br/>(HTTPS)"| LB
212+
LB --> Proxy
213+
Proxy --> Sentry
214+
Proxy --> Slack
215+
Proxy --> GitHub
216+
Proxy --> Grafana
217+
218+
style Proxy fill:#4a9,color:#fff
219+
style LB fill:#69b,color:#fff
220+
style Sentry fill:#f96
221+
style Slack fill:#f96
222+
style GitHub fill:#f96
223+
style Grafana fill:#f96
224+
```
225+
226+
## Further reading
227+
228+
- [Proxy mode](proxy-mode.md) — full technical guide on stdio/HTTP modes, endpoints, and configuration
229+
- [Proxy mode authentication](proxy-mode.md#authentication) — bearer tokens, forwarded user, and ACL rules
230+
- [Config file reference](../reference/config-file.md#server-authentication-serverauth) — serverAuth schema

docs/guides/proxy-mode.md

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -134,23 +134,27 @@ The HTTP server shuts down cleanly on `SIGTERM` or `SIGINT` (Ctrl+C). It stops a
134134

135135
Run one proxy server on shared infrastructure. Every developer connects to it:
136136

137-
```
138-
┌──────────────────────────────────────────────────┐
139-
│ Private server (team infra) │
140-
│ │
141-
│ mcp serve --http :8080 │
142-
│ ↓ proxy to backends │
143-
│ ┌─────────┐ ┌──────┐ ┌────────┐ ┌──────────┐ │
144-
│ │ Slack │ │Sentry│ │ GitHub │ │ Postgres │ │
145-
│ │ (token) │ │(token)│ │(token) │ │ (token) │ │
146-
│ └─────────┘ └──────┘ └────────┘ └──────────┘ │
147-
└──────────────────────────────────────────────────┘
148-
149-
Dev 1: mcp add --url http://mcp.internal:8080/mcp team
150-
Dev 2: mcp add --url http://mcp.internal:8080/mcp team
137+
```mermaid
138+
graph TB
139+
subgraph Server["Private server (team infra)"]
140+
Proxy["mcp serve --http :8080"]
141+
Slack["Slack (token)"]
142+
Sentry["Sentry (token)"]
143+
GitHub["GitHub (token)"]
144+
Postgres["Postgres (token)"]
145+
Proxy --> Slack
146+
Proxy --> Sentry
147+
Proxy --> GitHub
148+
Proxy --> Postgres
149+
end
150+
151+
D1["Dev 1"] -->|"mcp add --url http://mcp.internal:8080/mcp team"| Proxy
152+
D2["Dev 2"] -->|"mcp add --url http://mcp.internal:8080/mcp team"| Proxy
153+
154+
style Proxy fill:#4a9,color:#fff
151155
```
152156

153-
Tokens stay on the server. Developers just connect.
157+
Tokens stay on the server. Developers just connect. For a deeper look at the enterprise use case, see [Enterprise token management](enterprise-token-management.md).
154158

155159
## Client configuration
156160

0 commit comments

Comments
 (0)