Terminal — ai coding agent — ~/bowtie-internal
~/bowtie-internal $ bt-vibe
 
bt-vibe-coding-cli v1.0.40
Model: a-random-sota-model via LLM Gateway
 
MCP Servers:
notion (remote — OAuth)
slack (remote — OAuth)
bowtie-internal (remote — OAuth) — Not authenticated
 
You: Use the bowtie-internal MCP to look up ticket BT-4582
 

Agent: I'll look up that ticket for you.
 
tool get_internal_data
{ "ticket_id": "BT-4582" }
 
mcp bowtie-internal requires authentication
Server returned: 401 Unauthorized — Bearer token required
 
Agent: The bowtie-internal MCP server requires OAuth authentication.
Opening browser for SSO login...
 
◠ Waiting for OAuth callback on localhost:9876...
🔒 auth.bowtie.internal/login?client_id=mcp-internal&response_type=code&redirect_uri=http://localhost:9876/callback
Bowtie Internal Tools
Sign in to continue to MCP Authorization
● AWS Amazon Cognito Hosted UI
or
O Login with Company SSO Account
Click "Login with Company SSO" ↓
🔒 sso.bowtie.internal/app/bowtie-internal/sso/saml

Bowtie Life Insurance

Company Single Sign-On

SAML 2.0 · Federated Identity
🔒 Signing in to: internal-mcp.bowtie.internal
Forgot password?
Verifying credentials & redirecting to Cognito...
Click "Verify & Continue" ↓
🔒 localhost:9876/callback?code=aBcDe12345&state=pkce_xyz

Authorization Successful

OAuth 2.0 token received via PKCE flow

User gk@bt.com
Identity Provider O*ta SAML → Amazon Cognito
Access Token eyJhbG...Rk4x 🔒
Token Type Bearer (JWT)
Expires In 3600s
Scopes
openid read:tickets read:users
✅ Token sent to AI Coding Agent on localhost:9876. You can close this browser tab.
Terminal — ai coding agent — ~/bowtie-internal
... (OAuth flow completed in browser)
 
oauth ✓ Token received from Cognito
User: gk@bt.com
Scopes: openid, read:tickets, read:users
Expires: 3600s
 
MCP Servers:
notion (remote — OAuth)
slack (remote — OAuth)
bowtie-internal (remote — OAuth) — Authenticated as gk
 

Agent: Great, I'm now authenticated. Let me look up ticket BT-4582.
 
tool get_internal_data
// params { "ticket_id": "BT-4582" }
 
// output
{ "ticket_id": "BT-4582", "title": "Add claim status webhook for partner integration", "status": "In Progress", "assignee": "gk@bt.com", "priority": "High", "sprint": "Shape Up — Cycle 14" }
200 ✓ Returned as gk (JWT validated)
 
Agent: Here's ticket BT-4582:
Add claim status webhook for partner integration
Status: In Progress · Assigned: you · Priority: High
 
You: Now try to access the HR salary database
 

Agent: I'll try that query.
 
tool get_internal_data
// params { "resource": "hr_salaries", "query": "SELECT * FROM salaries" }
 
// output
{ "error": "forbidden", "message": "User gk@bt.com does not have scope 'read:hr_data'", "required_scope": "read:hr_data", "user_scopes": ["openid", "read:tickets", "read:users"], "status": 403 }
403 ✗ Forbidden — missing required scope
 
Agent: Access denied. Your token only grants read:tickets and read:users.
The HR salary database requires read:hr_data, which your role doesn't include.
 
This is OAuth working as intended:
The AI agent can only access what you can access. No more, no less.
 

 
⚠ Meanwhile, in the shared-API-key timeline...

💀 The "Old Way" — hardcoded credentials

Shared API key baked into every developer's mcp.json
Everyone gets admin-level access — HR data, financials, all of it
Audit log says "api-key-prod"who actually made the call?
Someone leaves? Rotate key → break everyone
Key leaked in a repo? Full access until someone notices
 
// .bt-vibe/mcp.json — the nightmare version { "bowtie-internal": { "type": "stdio", "command": "npx", "args": ["bowtie-mcp"], "env": { "API_KEY": "sk-prod-SHARED-KEY-123456", // 💀 everyone shares this "USERNAME": "admin", "PASSWORD": "B0wt1e2026!" // 💀 hardcoded in chat history } } }
 
✓ With OAuth MCP
// .bt-vibe/mcp.json — the OAuth version { "bowtie-internal": { "type": "http", "url": "https://internal-mcp.bowtie.internal" // That's it. No secrets. OAuth handles auth per-user. } }
 
Per-user identity — audit says exactly who
Scoped access — only what your role allows
Revoke one user — everyone else keeps working
No secrets in config — nothing to leak
SSO integration — same login as everything else
 
"If you can't do it in the UI, the agent can't do it via MCP"
Agent Con 2026 HK — Team Wide Vibe Coding
Scene 1 / 5 bowtie-internal MCP | OAuth 2.0 + PKCE ← p / n → · Space / Click