acuity-mcp-server 1.0.0
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 +541 -0
- package/dist/auth/device-flow.d.ts +46 -0
- package/dist/auth/device-flow.d.ts.map +1 -0
- package/dist/auth/device-flow.js +141 -0
- package/dist/auth/device-flow.js.map +1 -0
- package/dist/auth/http-auth.d.ts +25 -0
- package/dist/auth/http-auth.d.ts.map +1 -0
- package/dist/auth/http-auth.js +101 -0
- package/dist/auth/http-auth.js.map +1 -0
- package/dist/auth/jwt-validator.d.ts +20 -0
- package/dist/auth/jwt-validator.d.ts.map +1 -0
- package/dist/auth/jwt-validator.js +83 -0
- package/dist/auth/jwt-validator.js.map +1 -0
- package/dist/auth/token-storage.d.ts +88 -0
- package/dist/auth/token-storage.d.ts.map +1 -0
- package/dist/auth/token-storage.js +273 -0
- package/dist/auth/token-storage.js.map +1 -0
- package/dist/clients/hasura-client.d.ts +33 -0
- package/dist/clients/hasura-client.d.ts.map +1 -0
- package/dist/clients/hasura-client.js +79 -0
- package/dist/clients/hasura-client.js.map +1 -0
- package/dist/config/environments.d.ts +51 -0
- package/dist/config/environments.d.ts.map +1 -0
- package/dist/config/environments.js +183 -0
- package/dist/config/environments.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +593 -0
- package/dist/index.js.map +1 -0
- package/dist/server/http-server.d.ts +14 -0
- package/dist/server/http-server.d.ts.map +1 -0
- package/dist/server/http-server.js +167 -0
- package/dist/server/http-server.js.map +1 -0
- package/dist/server/mcp-core.d.ts +12 -0
- package/dist/server/mcp-core.d.ts.map +1 -0
- package/dist/server/mcp-core.js +200 -0
- package/dist/server/mcp-core.js.map +1 -0
- package/dist/tools/acuity-init.d.ts +84 -0
- package/dist/tools/acuity-init.d.ts.map +1 -0
- package/dist/tools/acuity-init.js +239 -0
- package/dist/tools/acuity-init.js.map +1 -0
- package/dist/tools/get-dashboard-summary.d.ts +96 -0
- package/dist/tools/get-dashboard-summary.d.ts.map +1 -0
- package/dist/tools/get-dashboard-summary.js +264 -0
- package/dist/tools/get-dashboard-summary.js.map +1 -0
- package/dist/tools/get-issue.d.ts +62 -0
- package/dist/tools/get-issue.d.ts.map +1 -0
- package/dist/tools/get-issue.js +150 -0
- package/dist/tools/get-issue.js.map +1 -0
- package/dist/tools/get-lesson-learned.d.ts +53 -0
- package/dist/tools/get-lesson-learned.d.ts.map +1 -0
- package/dist/tools/get-lesson-learned.js +117 -0
- package/dist/tools/get-lesson-learned.js.map +1 -0
- package/dist/tools/get-lookup-values.d.ts +41 -0
- package/dist/tools/get-lookup-values.d.ts.map +1 -0
- package/dist/tools/get-lookup-values.js +127 -0
- package/dist/tools/get-lookup-values.js.map +1 -0
- package/dist/tools/get-project.d.ts +131 -0
- package/dist/tools/get-project.d.ts.map +1 -0
- package/dist/tools/get-project.js +340 -0
- package/dist/tools/get-project.js.map +1 -0
- package/dist/tools/get-risk.d.ts +65 -0
- package/dist/tools/get-risk.d.ts.map +1 -0
- package/dist/tools/get-risk.js +173 -0
- package/dist/tools/get-risk.js.map +1 -0
- package/dist/tools/get-status-reports.d.ts +46 -0
- package/dist/tools/get-status-reports.d.ts.map +1 -0
- package/dist/tools/get-status-reports.js +151 -0
- package/dist/tools/get-status-reports.js.map +1 -0
- package/dist/tools/init-auth.d.ts +47 -0
- package/dist/tools/init-auth.d.ts.map +1 -0
- package/dist/tools/init-auth.js +213 -0
- package/dist/tools/init-auth.js.map +1 -0
- package/dist/tools/list-issues.d.ts +134 -0
- package/dist/tools/list-issues.d.ts.map +1 -0
- package/dist/tools/list-issues.js +285 -0
- package/dist/tools/list-issues.js.map +1 -0
- package/dist/tools/list-lessons-learned.d.ts +79 -0
- package/dist/tools/list-lessons-learned.d.ts.map +1 -0
- package/dist/tools/list-lessons-learned.js +155 -0
- package/dist/tools/list-lessons-learned.js.map +1 -0
- package/dist/tools/list-projects.d.ts +200 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +396 -0
- package/dist/tools/list-projects.js.map +1 -0
- package/dist/tools/list-risks.d.ts +166 -0
- package/dist/tools/list-risks.d.ts.map +1 -0
- package/dist/tools/list-risks.js +356 -0
- package/dist/tools/list-risks.js.map +1 -0
- package/dist/tools/search-projects.d.ts +90 -0
- package/dist/tools/search-projects.d.ts.map +1 -0
- package/dist/tools/search-projects.js +191 -0
- package/dist/tools/search-projects.js.map +1 -0
- package/dist/utils/formatters.d.ts +12 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +28 -0
- package/dist/utils/formatters.js.map +1 -0
- package/openapi.yaml +194 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# Acuity MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for Acuity Project Management. Enables LLMs like Claude and Microsoft Copilot to query project data, generate reports, and provide AI-assisted insights.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dual Transport Support**:
|
|
8
|
+
- **stdio** - For local clients (Claude Desktop, Cursor)
|
|
9
|
+
- **HTTP (Streamable HTTP)** - For remote clients (Microsoft Copilot Studio, remote Claude)
|
|
10
|
+
- **Authentication**: OAuth 2.0 Device Authorization Flow with two-phase verification
|
|
11
|
+
- **SSO Support**: Enterprise SSO (SAML, Azure AD, Okta) works automatically
|
|
12
|
+
- **Authorization**: Leverages Hasura row-level security (users only see their accessible data)
|
|
13
|
+
- **Read-Only**: MVP focuses on safe, read-only queries
|
|
14
|
+
- **Secure Storage**: Credentials stored in system keychain (macOS Keychain, Windows Credential Manager)
|
|
15
|
+
- **Container Ready**: Dockerfile included for Azure Container Apps deployment
|
|
16
|
+
- **Core Tools**:
|
|
17
|
+
- `acuity_init` - Initialize context and get company info
|
|
18
|
+
- `list_projects` / `get_project` / `search_projects` - Project management
|
|
19
|
+
- `get_status_reports` - Project status reports
|
|
20
|
+
- `list_risks` / `get_risk` - Risk management
|
|
21
|
+
- `list_issues` / `get_issue` - Issue tracking
|
|
22
|
+
- `list_lessons_learned` / `get_lesson_learned` - Lessons learned
|
|
23
|
+
- `get_lookup_values` - Departments, categories, lifecycles
|
|
24
|
+
- `get_dashboard_summary` - Portfolio dashboard data
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
|
|
28
|
+
- Node.js 20.x
|
|
29
|
+
- Acuity development environment running:
|
|
30
|
+
- PostgreSQL (port 5433)
|
|
31
|
+
- Hasura (port 8080)
|
|
32
|
+
- Auth0 configuration
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Install dependencies
|
|
38
|
+
npm install
|
|
39
|
+
|
|
40
|
+
# Build the server
|
|
41
|
+
npm run build
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
### 1. Authenticate (One-time setup)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Run the authentication flow
|
|
50
|
+
npm start init
|
|
51
|
+
# Or directly: node dist/index.js init
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This will:
|
|
55
|
+
1. Open your browser to Auth0 login page
|
|
56
|
+
2. Display a verification code (e.g., `ABCD-EFGH`)
|
|
57
|
+
3. After you log in, credentials are stored securely in your system keychain
|
|
58
|
+
|
|
59
|
+
### 2. Check Authentication Status
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm start status
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. Configure Your MCP Client
|
|
66
|
+
|
|
67
|
+
Add to Claude Desktop, Cursor, or other MCP clients (see Configuration section below).
|
|
68
|
+
|
|
69
|
+
That's it! No manual JWT token copying required.
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
### Environment Variables
|
|
74
|
+
|
|
75
|
+
Required in `.env.development`:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Auth0
|
|
79
|
+
AUTH0_DOMAIN=auth.acuityppm.com
|
|
80
|
+
AUTH0_MANAGEMENT_DOMAIN=acuityppm.auth0.com
|
|
81
|
+
AUTH0_MCP_CLIENT_ID=your-native-app-client-id # Optional: dedicated MCP client
|
|
82
|
+
REACT_APP_AUTH0_CLIENT_ID=your-client-id # Fallback if AUTH0_MCP_CLIENT_ID not set
|
|
83
|
+
|
|
84
|
+
# Hasura
|
|
85
|
+
HASURA_GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql
|
|
86
|
+
|
|
87
|
+
# MCP Server
|
|
88
|
+
NODE_ENV=development
|
|
89
|
+
LOG_LEVEL=debug # Set to 'debug' for verbose logging
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Authentication Methods
|
|
93
|
+
|
|
94
|
+
#### Method 1: Device Authorization Flow (Recommended)
|
|
95
|
+
|
|
96
|
+
No manual token management required! Run `npm start init` once and credentials are stored securely.
|
|
97
|
+
|
|
98
|
+
Benefits:
|
|
99
|
+
- No manual JWT copying
|
|
100
|
+
- Automatic token refresh
|
|
101
|
+
- Credentials stored in system keychain
|
|
102
|
+
- Works until you logout or revoke access
|
|
103
|
+
|
|
104
|
+
#### Method 2: Legacy JWT Token (Deprecated)
|
|
105
|
+
|
|
106
|
+
For backward compatibility, you can still use `USER_JWT` environment variable:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
USER_JWT=your-jwt-token-here
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
To get a JWT token manually:
|
|
113
|
+
1. Log into Acuity web app (http://localhost:3000)
|
|
114
|
+
2. Open browser DevTools → Application → Local Storage
|
|
115
|
+
3. Find the Auth0 token (key like `@@auth0spajs@@::...`)
|
|
116
|
+
4. Copy the `id_token` value
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
### Development Mode
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Start in watch mode (auto-reload on changes)
|
|
124
|
+
npm run dev
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Build and Run
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Build TypeScript
|
|
131
|
+
npm run build
|
|
132
|
+
|
|
133
|
+
# Run built version
|
|
134
|
+
npm start
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Type Checking
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run typecheck
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## CLI Commands
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Start stdio server (for Claude Desktop, Cursor)
|
|
147
|
+
npm start
|
|
148
|
+
|
|
149
|
+
# Start HTTP server (for Copilot Studio, remote clients)
|
|
150
|
+
npm start http
|
|
151
|
+
npm start http --port 8080 # custom port
|
|
152
|
+
|
|
153
|
+
# Authenticate via browser (Device Authorization Flow)
|
|
154
|
+
npm start init
|
|
155
|
+
|
|
156
|
+
# Check authentication status
|
|
157
|
+
npm start status
|
|
158
|
+
|
|
159
|
+
# Remove stored credentials
|
|
160
|
+
npm start logout
|
|
161
|
+
|
|
162
|
+
# Show help
|
|
163
|
+
npm start help
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## HTTP Server Mode (Remote Clients)
|
|
167
|
+
|
|
168
|
+
The HTTP mode enables remote clients like Microsoft Copilot Studio and remote Claude instances to connect via Streamable HTTP transport.
|
|
169
|
+
|
|
170
|
+
### Starting HTTP Server
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# Build first
|
|
174
|
+
npm run build
|
|
175
|
+
|
|
176
|
+
# Start on default port 3000
|
|
177
|
+
npm start http
|
|
178
|
+
|
|
179
|
+
# Start on custom port
|
|
180
|
+
npm start http --port 8080
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Endpoints
|
|
184
|
+
|
|
185
|
+
| Endpoint | Method | Description |
|
|
186
|
+
|----------|--------|-------------|
|
|
187
|
+
| `/mcp` | POST | MCP JSON-RPC requests |
|
|
188
|
+
| `/mcp` | GET | SSE stream (server-to-client) |
|
|
189
|
+
| `/mcp` | DELETE | Close session |
|
|
190
|
+
| `/health` | GET | Health check |
|
|
191
|
+
| `/openapi.yaml` | GET | OpenAPI schema |
|
|
192
|
+
|
|
193
|
+
### Authentication (HTTP Mode)
|
|
194
|
+
|
|
195
|
+
HTTP requests require one of:
|
|
196
|
+
|
|
197
|
+
1. **OAuth 2.0 Bearer Token** (recommended):
|
|
198
|
+
```bash
|
|
199
|
+
curl -X POST http://localhost:3000/mcp \
|
|
200
|
+
-H "Authorization: Bearer <auth0-jwt-token>" \
|
|
201
|
+
-H "Content-Type: application/json" \
|
|
202
|
+
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
2. **API Key** (for service-to-service):
|
|
206
|
+
```bash
|
|
207
|
+
# Set environment variables first:
|
|
208
|
+
# MCP_API_KEY=your-secret-key
|
|
209
|
+
# MCP_SERVICE_EMAIL=service@example.com
|
|
210
|
+
|
|
211
|
+
curl -X POST http://localhost:3000/mcp \
|
|
212
|
+
-H "x-api-key: your-secret-key" \
|
|
213
|
+
-H "Content-Type: application/json" \
|
|
214
|
+
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Docker Deployment
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Build image
|
|
221
|
+
docker build -t acuity-mcp-server .
|
|
222
|
+
|
|
223
|
+
# Run container
|
|
224
|
+
docker run -p 3000:3000 \
|
|
225
|
+
-e AUTH0_DOMAIN=acuityppm.auth0.com \
|
|
226
|
+
-e HASURA_GRAPHQL_ENDPOINT=https://api.acuityppm.com/v1/graphql \
|
|
227
|
+
acuity-mcp-server
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Azure Container Apps Deployment
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Using Azure Developer CLI
|
|
234
|
+
azd init
|
|
235
|
+
azd up
|
|
236
|
+
|
|
237
|
+
# Or manual deployment
|
|
238
|
+
az containerapp up \
|
|
239
|
+
--name acuity-mcp \
|
|
240
|
+
--resource-group your-rg \
|
|
241
|
+
--image your-registry/acuity-mcp-server:latest \
|
|
242
|
+
--target-port 3000 \
|
|
243
|
+
--ingress external
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Microsoft Copilot Studio Integration
|
|
247
|
+
|
|
248
|
+
1. Go to **Copilot Studio** → **Tools** → **Add tool** → **Model Context Protocol**
|
|
249
|
+
2. Enter server URL: `https://<your-app>.azurecontainerapps.io/mcp`
|
|
250
|
+
3. Select authentication: **OAuth 2.0**
|
|
251
|
+
4. Configure Auth0:
|
|
252
|
+
- Authorization URL: `https://acuityppm.auth0.com/authorize`
|
|
253
|
+
- Token URL: `https://acuityppm.auth0.com/oauth/token`
|
|
254
|
+
5. Click **Create** and test connection
|
|
255
|
+
|
|
256
|
+
## Claude Desktop Configuration
|
|
257
|
+
|
|
258
|
+
### Option 1: Using npx (Recommended after npm publish)
|
|
259
|
+
|
|
260
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"mcpServers": {
|
|
265
|
+
"acuity": {
|
|
266
|
+
"command": "npx",
|
|
267
|
+
"args": ["-y", "acuity-mcp-server"],
|
|
268
|
+
"env": {
|
|
269
|
+
"ACUITY_ENV": "US-1"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Available environments:**
|
|
277
|
+
- `US-1` - Production US
|
|
278
|
+
- `US-staging` - Staging US
|
|
279
|
+
- `EU-1` - Production EU
|
|
280
|
+
- `UAE-1` - Production UAE
|
|
281
|
+
- `US-2`, `US-3`, `US-5`, `US-6`, `US-7` - US client instances
|
|
282
|
+
- `EU-2`, `EU-3`, `EU-4` - EU client instances
|
|
283
|
+
- `dev` - Local development
|
|
284
|
+
|
|
285
|
+
### Option 2: Local Development
|
|
286
|
+
|
|
287
|
+
For development with local source code:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"mcpServers": {
|
|
292
|
+
"acuity": {
|
|
293
|
+
"command": "node",
|
|
294
|
+
"args": ["/path/to/acuity/mcp-server/dist/index.js"],
|
|
295
|
+
"env": {
|
|
296
|
+
"ACUITY_ENV": "dev"
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Important:** `ACUITY_ENV` is required - you must specify which environment to connect to.
|
|
304
|
+
|
|
305
|
+
### First-Time Login (Two-Phase Flow)
|
|
306
|
+
|
|
307
|
+
In Claude Desktop, simply say:
|
|
308
|
+
```
|
|
309
|
+
"Login to Acuity"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Phase 1 - Get verification code:**
|
|
313
|
+
1. Claude shows: `🔐 Verification Code: XXXX-XXXX`
|
|
314
|
+
2. A browser window opens automatically
|
|
315
|
+
3. **Verify the code in browser matches the code in Claude!**
|
|
316
|
+
4. Log in with your Acuity credentials (or use SSO if configured)
|
|
317
|
+
|
|
318
|
+
**Phase 2 - Complete authentication:**
|
|
319
|
+
1. After logging in, say: `"continue login"` or `"I've logged in"`
|
|
320
|
+
2. Claude verifies and shows: `✅ Login Successful!`
|
|
321
|
+
|
|
322
|
+
Credentials are stored securely in your system keychain.
|
|
323
|
+
|
|
324
|
+
### Example Queries
|
|
325
|
+
|
|
326
|
+
Try these in Claude Desktop:
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
"Login to Acuity" # First-time authentication
|
|
330
|
+
|
|
331
|
+
"Show me my projects" # List your projects
|
|
332
|
+
|
|
333
|
+
"Check my auth status" # Verify authentication
|
|
334
|
+
|
|
335
|
+
"Logout from Acuity" # Clear credentials
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Available Tools
|
|
339
|
+
|
|
340
|
+
### Authentication Tools
|
|
341
|
+
|
|
342
|
+
#### acuity_login
|
|
343
|
+
Login to Acuity. Opens a browser for secure OAuth login.
|
|
344
|
+
|
|
345
|
+
#### acuity_logout
|
|
346
|
+
Logout from Acuity and clear stored credentials.
|
|
347
|
+
|
|
348
|
+
#### acuity_auth_status
|
|
349
|
+
Check current authentication status.
|
|
350
|
+
|
|
351
|
+
### Data Tools
|
|
352
|
+
|
|
353
|
+
#### list_projects
|
|
354
|
+
|
|
355
|
+
Lists all projects the user has access to.
|
|
356
|
+
|
|
357
|
+
**Parameters:**
|
|
358
|
+
- `state` (optional): Filter by state (active, hold, completed, cancelled, pending)
|
|
359
|
+
- `health_status` (optional): Filter by health (green, yellow, red, gray)
|
|
360
|
+
- `portfolio_id` (optional): Filter by portfolio
|
|
361
|
+
- `limit` (optional, default: 20): Max results
|
|
362
|
+
- `offset` (optional, default: 0): Pagination offset
|
|
363
|
+
|
|
364
|
+
#### get_status_reports
|
|
365
|
+
|
|
366
|
+
Get status reports for projects.
|
|
367
|
+
|
|
368
|
+
**Parameters:**
|
|
369
|
+
- `project_id` (optional): Specific project ID
|
|
370
|
+
- `limit` (optional, default: 10): Max results
|
|
371
|
+
|
|
372
|
+
## Architecture
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
┌──────────────────┐ ┌───────────────────┐
|
|
376
|
+
│ Claude Desktop │ │ Microsoft Copilot │
|
|
377
|
+
│ Cursor, etc. │ │ Remote Claude │
|
|
378
|
+
└────────┬─────────┘ └─────────┬─────────┘
|
|
379
|
+
│ stdio │ HTTPS
|
|
380
|
+
│ │
|
|
381
|
+
▼ ▼
|
|
382
|
+
┌─────────────────────────────────────────────┐
|
|
383
|
+
│ Acuity MCP Server │
|
|
384
|
+
│ ┌─────────────┐ ┌────────────────────┐ │
|
|
385
|
+
│ │ StdioServer │ │ HTTP Server │ │
|
|
386
|
+
│ │ Transport │ │ (Streamable HTTP) │ │
|
|
387
|
+
│ └──────┬──────┘ └──────────┬─────────┘ │
|
|
388
|
+
│ │ │ │
|
|
389
|
+
│ └──────────┬───────────┘ │
|
|
390
|
+
│ ▼ │
|
|
391
|
+
│ ┌─────────────────────────────────────┐ │
|
|
392
|
+
│ │ MCP Core │ │
|
|
393
|
+
│ │ - Tool Handlers │ │
|
|
394
|
+
│ │ - Session Management │ │
|
|
395
|
+
│ │ - JWT/API Key Validation │ │
|
|
396
|
+
│ └─────────────────┬───────────────────┘ │
|
|
397
|
+
└────────────────────┼────────────────────────┘
|
|
398
|
+
│
|
|
399
|
+
▼
|
|
400
|
+
┌─────────────────┐
|
|
401
|
+
│ Hasura GraphQL │
|
|
402
|
+
│ + Row Security │
|
|
403
|
+
└────────┬────────┘
|
|
404
|
+
│
|
|
405
|
+
▼
|
|
406
|
+
┌─────────────────┐
|
|
407
|
+
│ PostgreSQL │
|
|
408
|
+
└─────────────────┘
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Security
|
|
412
|
+
|
|
413
|
+
- ✅ OAuth 2.0 Device Authorization Flow with two-phase code verification
|
|
414
|
+
- ✅ Enterprise SSO support (SAML, Azure AD, Okta)
|
|
415
|
+
- ✅ ID Token (JWT) used for Hasura authentication
|
|
416
|
+
- ✅ Credentials stored in system keychain (not in files)
|
|
417
|
+
- ✅ Automatic token refresh (no manual intervention)
|
|
418
|
+
- ✅ Row-level security via Hasura (X-Hasura-Email)
|
|
419
|
+
- ✅ Read-only operations
|
|
420
|
+
- ✅ User scoped to their accessible portfolios
|
|
421
|
+
- ✅ No cross-company data leakage
|
|
422
|
+
|
|
423
|
+
## Troubleshooting
|
|
424
|
+
|
|
425
|
+
### "No credentials stored. Run 'init' to authenticate."
|
|
426
|
+
- You haven't authenticated yet
|
|
427
|
+
- Run `npm start init` to authenticate via browser
|
|
428
|
+
|
|
429
|
+
### "Token expired and no refresh token available"
|
|
430
|
+
- Your refresh token has been revoked or expired
|
|
431
|
+
- Run `npm start init` to re-authenticate
|
|
432
|
+
|
|
433
|
+
### "Failed to refresh token"
|
|
434
|
+
- Check your internet connection
|
|
435
|
+
- Auth0 may be temporarily unavailable
|
|
436
|
+
- Run `npm start init` to get fresh credentials
|
|
437
|
+
|
|
438
|
+
### "Could not open browser automatically"
|
|
439
|
+
- Open the displayed URL manually in your browser
|
|
440
|
+
- Enter the verification code shown in terminal
|
|
441
|
+
|
|
442
|
+
### "Invalid JWT token"
|
|
443
|
+
- Run `npm start status` to check token status
|
|
444
|
+
- If expired, run `npm start init` to re-authenticate
|
|
445
|
+
- Verify Auth0 domain configuration matches
|
|
446
|
+
|
|
447
|
+
### "GraphQL query failed"
|
|
448
|
+
- Check Hasura is running (`docker-compose up`)
|
|
449
|
+
- Verify endpoint: `http://localhost:8080/v1/graphql`
|
|
450
|
+
- Check user has access to portfolios
|
|
451
|
+
|
|
452
|
+
### "Project not found"
|
|
453
|
+
- User may not have access to that project
|
|
454
|
+
- Check portfolio memberships in database
|
|
455
|
+
- Verify project ID is correct
|
|
456
|
+
|
|
457
|
+
### Legacy: "JWT token is required" (USER_JWT mode)
|
|
458
|
+
- Ensure `USER_JWT` is set in environment
|
|
459
|
+
- Token must be valid (not expired)
|
|
460
|
+
- Consider switching to Device Flow: `npm start init`
|
|
461
|
+
|
|
462
|
+
## Development
|
|
463
|
+
|
|
464
|
+
### Project Structure
|
|
465
|
+
|
|
466
|
+
```
|
|
467
|
+
mcp-server/
|
|
468
|
+
├── src/
|
|
469
|
+
│ ├── index.ts # Entry point + CLI (stdio & http modes)
|
|
470
|
+
│ ├── server/
|
|
471
|
+
│ │ ├── http-server.ts # Express + Streamable HTTP transport
|
|
472
|
+
│ │ └── mcp-core.ts # Shared MCP server logic
|
|
473
|
+
│ ├── auth/
|
|
474
|
+
│ │ ├── jwt-validator.ts # Auth0 JWT validation
|
|
475
|
+
│ │ ├── device-flow.ts # OAuth 2.0 Device Authorization Flow
|
|
476
|
+
│ │ ├── token-storage.ts # System keychain credential storage
|
|
477
|
+
│ │ └── http-auth.ts # HTTP OAuth/API key middleware
|
|
478
|
+
│ ├── clients/
|
|
479
|
+
│ │ └── hasura-client.ts # GraphQL client (uses ID Token for auth)
|
|
480
|
+
│ ├── tools/
|
|
481
|
+
│ │ ├── init-auth.ts # Login/logout/status tools
|
|
482
|
+
│ │ ├── acuity-init.ts # Initialize context
|
|
483
|
+
│ │ ├── list-projects.ts # List/filter projects
|
|
484
|
+
│ │ ├── get-project.ts # Get project details
|
|
485
|
+
│ │ ├── search-projects.ts # Search projects
|
|
486
|
+
│ │ ├── get-status-reports.ts
|
|
487
|
+
│ │ ├── list-risks.ts / get-risk.ts
|
|
488
|
+
│ │ ├── list-issues.ts / get-issue.ts
|
|
489
|
+
│ │ ├── list-lessons-learned.ts / get-lesson-learned.ts
|
|
490
|
+
│ │ ├── get-lookup-values.ts
|
|
491
|
+
│ │ └── get-dashboard-summary.ts
|
|
492
|
+
│ ├── types/
|
|
493
|
+
│ │ └── project.ts # TypeScript types
|
|
494
|
+
│ └── utils/
|
|
495
|
+
│ └── formatters.ts # Response formatting
|
|
496
|
+
├── openapi.yaml # OpenAPI schema for Copilot Studio
|
|
497
|
+
├── Dockerfile # Container build
|
|
498
|
+
├── package.json
|
|
499
|
+
├── tsconfig.json
|
|
500
|
+
└── README.md
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Adding a New Tool
|
|
504
|
+
|
|
505
|
+
1. Create tool file in `src/tools/`
|
|
506
|
+
2. Define GraphQL query
|
|
507
|
+
3. Implement handler function
|
|
508
|
+
4. Export tool definition with schema
|
|
509
|
+
5. Register in `src/index.ts`
|
|
510
|
+
|
|
511
|
+
Example:
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
// src/tools/my-tool.ts
|
|
515
|
+
export const myTool = {
|
|
516
|
+
name: 'my_tool',
|
|
517
|
+
description: 'What this tool does',
|
|
518
|
+
inputSchema: {
|
|
519
|
+
type: 'object',
|
|
520
|
+
properties: {
|
|
521
|
+
param: { type: 'string' }
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
export async function myToolHandler(input, hasuraClient) {
|
|
527
|
+
// Implementation
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
## References
|
|
532
|
+
|
|
533
|
+
- [Authentication Guide](../.claude/docs/MCP_AUTH.md)
|
|
534
|
+
- [Planning Document](../docs/MCP/planning.md)
|
|
535
|
+
- [MCP Documentation](https://modelcontextprotocol.io)
|
|
536
|
+
- [Hasura Permissions](../hasura/metadata/databases/default/tables/)
|
|
537
|
+
- [Auth0 Device Flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow)
|
|
538
|
+
|
|
539
|
+
## License
|
|
540
|
+
|
|
541
|
+
ISC
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 Device Authorization Flow for Auth0
|
|
3
|
+
*
|
|
4
|
+
* Implements the Device Code flow for CLI/MCP authentication
|
|
5
|
+
* Reference: https://auth0.com/docs/get-started/authentication-and-authorization-flow/device-authorization-flow
|
|
6
|
+
*/
|
|
7
|
+
export interface DeviceCodeResponse {
|
|
8
|
+
device_code: string;
|
|
9
|
+
user_code: string;
|
|
10
|
+
verification_uri: string;
|
|
11
|
+
verification_uri_complete: string;
|
|
12
|
+
expires_in: number;
|
|
13
|
+
interval: number;
|
|
14
|
+
}
|
|
15
|
+
export interface TokenResponse {
|
|
16
|
+
access_token: string;
|
|
17
|
+
refresh_token?: string;
|
|
18
|
+
id_token?: string;
|
|
19
|
+
token_type: string;
|
|
20
|
+
expires_in: number;
|
|
21
|
+
scope?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface DeviceFlowConfig {
|
|
24
|
+
domain: string;
|
|
25
|
+
clientId: string;
|
|
26
|
+
audience?: string;
|
|
27
|
+
scope?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Request a device code from Auth0
|
|
31
|
+
*/
|
|
32
|
+
export declare function requestDeviceCode(config: DeviceFlowConfig): Promise<DeviceCodeResponse>;
|
|
33
|
+
/**
|
|
34
|
+
* Poll for access token
|
|
35
|
+
* Returns token when user completes authorization, or throws on timeout/error
|
|
36
|
+
*/
|
|
37
|
+
export declare function pollForToken(config: DeviceFlowConfig, deviceCode: string, interval: number, expiresIn: number, onPoll?: () => void): Promise<TokenResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Refresh an access token using a refresh token
|
|
40
|
+
*/
|
|
41
|
+
export declare function refreshAccessToken(config: DeviceFlowConfig, refreshToken: string): Promise<TokenResponse>;
|
|
42
|
+
/**
|
|
43
|
+
* Run the complete Device Authorization Flow
|
|
44
|
+
*/
|
|
45
|
+
export declare function runDeviceFlow(config: DeviceFlowConfig): Promise<TokenResponse>;
|
|
46
|
+
//# sourceMappingURL=device-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/auth/device-flow.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,yBAAyB,EAAE,MAAM,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA0B7F;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,IAAI,GAClB,OAAO,CAAC,aAAa,CAAC,CAwDxB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EACxB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC,CAuBxB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAsCpF"}
|