@mag.ni/process 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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +421 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +208 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +701 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/agents.d.ts +39 -0
- package/dist/tools/agents.d.ts.map +1 -0
- package/dist/tools/agents.js +141 -0
- package/dist/tools/agents.js.map +1 -0
- package/dist/tools/design.d.ts +175 -0
- package/dist/tools/design.d.ts.map +1 -0
- package/dist/tools/design.js +1172 -0
- package/dist/tools/design.js.map +1 -0
- package/dist/tools/index.d.ts +27 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +131 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/session.d.ts +77 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +285 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/work.d.ts +99 -0
- package/dist/tools/work.d.ts.map +1 -0
- package/dist/tools/work.js +702 -0
- package/dist/tools/work.js.map +1 -0
- package/dist/types.d.ts +264 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mag.ni
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @mag.ni/process
|
|
2
|
+
|
|
3
|
+
MCP server for [Processes](https://process.mag.ni) — a platform where users design structured workflows that guide AI agents through sequential tasks, decision points, and reporting.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
Add to your `.mcp.json` (project root or `~/.claude.json`):
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"process": {
|
|
13
|
+
"command": "npx",
|
|
14
|
+
"args": ["-y", "@mag.ni/process"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
On first use, a browser window opens for authentication. After signing in, the token is stored locally and reused automatically.
|
|
21
|
+
|
|
22
|
+
### Windows
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"process": {
|
|
28
|
+
"command": "cmd",
|
|
29
|
+
"args": ["/c", "npx", "-y", "@mag.ni/process"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Auto-Updates
|
|
36
|
+
|
|
37
|
+
Using `npx -y` ensures you always run the latest version. Updates are picked up automatically when the MCP server restarts.
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
All settings have sensible defaults. Override via the `env` block in `.mcp.json` if needed:
|
|
42
|
+
|
|
43
|
+
| Variable | Default | Description |
|
|
44
|
+
|----------|---------|-------------|
|
|
45
|
+
| `PROCESSES_API_URL` | `https://process.mag.ni` | Process API endpoint |
|
|
46
|
+
| `PROCESSES_AUTH_URL` | `https://auth.mag.ni` | OAuth authorization server |
|
|
47
|
+
| `PROCESSES_OAUTH_CLIENT_ID` | `process-mcp` | OAuth client ID |
|
|
48
|
+
| `PROCESSES_OAUTH_CALLBACK_PORT` | `19823` | Local port for OAuth callback |
|
|
49
|
+
| `SENTRY_DSN` | _(disabled)_ | Error reporting endpoint |
|
|
50
|
+
|
|
51
|
+
Example with custom API URL (e.g. for staging):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"process": {
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["-y", "@mag.ni/process"],
|
|
59
|
+
"env": {
|
|
60
|
+
"PROCESSES_API_URL": "https://process.office.mag.ni",
|
|
61
|
+
"PROCESSES_AUTH_URL": "https://auth.office.mag.ni"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Available Tools
|
|
69
|
+
|
|
70
|
+
### Agent Management
|
|
71
|
+
| Tool | Description |
|
|
72
|
+
|------|-------------|
|
|
73
|
+
| `list_agent_users` | List all agent identities |
|
|
74
|
+
| `create_agent_user` | Create a new agent identity |
|
|
75
|
+
| `delete_agent_user` | Delete an unclaimed agent identity |
|
|
76
|
+
|
|
77
|
+
### Session Management
|
|
78
|
+
| Tool | Description |
|
|
79
|
+
|------|-------------|
|
|
80
|
+
| `claim_user` | Claim an agent identity to start working |
|
|
81
|
+
| `heartbeat` | Extend session lease |
|
|
82
|
+
| `release_user` | Release claimed identity |
|
|
83
|
+
| `logout` | Clear session and stored credentials |
|
|
84
|
+
|
|
85
|
+
### Workflow Execution
|
|
86
|
+
| Tool | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `get_my_instances` | Get workflow instances assigned to you |
|
|
89
|
+
| `get_instance_details` | Get current step, history, and metadata |
|
|
90
|
+
| `get_available_actions` | Get actions at the current step |
|
|
91
|
+
| `take_action` | Execute an action to advance the workflow |
|
|
92
|
+
| `get_diagram_context` | Get full workflow structure |
|
|
93
|
+
| `get_instance_notes` | Get notes on an instance |
|
|
94
|
+
| `add_instance_note` | Add a note to an instance |
|
|
95
|
+
| `get_node_details` | Get detailed instructions for steps |
|
|
96
|
+
|
|
97
|
+
### Diagram Design
|
|
98
|
+
| Tool | Description |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `enter_design_mode` | Enter design mode |
|
|
101
|
+
| `exit_design_mode` | Exit design mode |
|
|
102
|
+
| `list_diagrams` | List available diagrams |
|
|
103
|
+
| `get_diagram` | Get diagram details |
|
|
104
|
+
| `create_diagram` | Create a new diagram |
|
|
105
|
+
| `update_diagram` | Update diagram properties |
|
|
106
|
+
| `create_node` | Add a node to a diagram |
|
|
107
|
+
| `update_node` | Modify a node |
|
|
108
|
+
| `delete_node` | Remove a node |
|
|
109
|
+
| `create_arrow` | Connect two nodes |
|
|
110
|
+
| `update_arrow` | Modify a connection |
|
|
111
|
+
| `delete_arrow` | Remove a connection |
|
|
112
|
+
| `delete_diagram` | Delete a diagram |
|
|
113
|
+
| `duplicate_diagram` | Copy a diagram |
|
|
114
|
+
|
|
115
|
+
## Requirements
|
|
116
|
+
|
|
117
|
+
- Node.js 18+
|
|
118
|
+
- An MCP-compatible client (e.g. [Claude Code](https://claude.com/claude-code))
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Processes - OAuth2 Authorization Code + PKCE Authentication
|
|
3
|
+
*
|
|
4
|
+
* Authenticates the MCP server as the user who runs it (like `gh auth login`).
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Check for stored refresh token in ~/.config/mcp-processes/tokens.json
|
|
7
|
+
* 2. If no token: start local HTTP server, open browser to auth server
|
|
8
|
+
* 3. User logs in → auth server redirects to http://localhost:{port}/callback
|
|
9
|
+
* 4. Exchange auth code for access_token + refresh_token
|
|
10
|
+
* 5. Store refresh token locally for future sessions
|
|
11
|
+
*/
|
|
12
|
+
export interface AuthConfig {
|
|
13
|
+
authUrl: string;
|
|
14
|
+
clientId: string;
|
|
15
|
+
clientSecret?: string;
|
|
16
|
+
callbackPort: number;
|
|
17
|
+
scopes: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function clearStoredTokens(authUrl?: string): void;
|
|
20
|
+
export declare class OAuthManager {
|
|
21
|
+
private config;
|
|
22
|
+
private accessToken;
|
|
23
|
+
private tokenExpiresAt;
|
|
24
|
+
private refreshToken;
|
|
25
|
+
constructor(config: AuthConfig);
|
|
26
|
+
/**
|
|
27
|
+
* Ensure we have a valid access token.
|
|
28
|
+
* If no token exists, triggers the browser-based login flow.
|
|
29
|
+
* If token is expired, uses refresh token to get a new one.
|
|
30
|
+
*/
|
|
31
|
+
ensureAccessToken(): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Start the interactive browser-based login flow
|
|
34
|
+
*/
|
|
35
|
+
private interactiveLogin;
|
|
36
|
+
/**
|
|
37
|
+
* Get the current access token (may be null if not yet authenticated)
|
|
38
|
+
*/
|
|
39
|
+
getAccessToken(): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Check if we have a stored refresh token (can authenticate without browser)
|
|
42
|
+
*/
|
|
43
|
+
hasStoredCredentials(): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Clear all in-memory tokens and remove stored credentials for this environment.
|
|
46
|
+
* After calling this, the next ensureAccessToken() will trigger interactive login.
|
|
47
|
+
*/
|
|
48
|
+
clearCredentials(): void;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA+BH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AA+CD,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAmBxD;AAgRD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,YAAY,CAAuB;gBAE/B,MAAM,EAAE,UAAU;IAU9B;;;;OAIG;IACG,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAmC1C;;OAEG;YACW,gBAAgB;IAkD9B;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI;IAO/B;;OAEG;IACH,oBAAoB,IAAI,OAAO;IAI/B;;;OAGG;IACH,gBAAgB,IAAI,IAAI;CAMzB"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Processes - OAuth2 Authorization Code + PKCE Authentication
|
|
3
|
+
*
|
|
4
|
+
* Authenticates the MCP server as the user who runs it (like `gh auth login`).
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Check for stored refresh token in ~/.config/mcp-processes/tokens.json
|
|
7
|
+
* 2. If no token: start local HTTP server, open browser to auth server
|
|
8
|
+
* 3. User logs in → auth server redirects to http://localhost:{port}/callback
|
|
9
|
+
* 4. Exchange auth code for access_token + refresh_token
|
|
10
|
+
* 5. Store refresh token locally for future sessions
|
|
11
|
+
*/
|
|
12
|
+
import * as http from 'node:http';
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import * as os from 'node:os';
|
|
16
|
+
import * as crypto from 'node:crypto';
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Token Storage
|
|
20
|
+
// =============================================================================
|
|
21
|
+
function getTokenFilePath() {
|
|
22
|
+
const configDir = process.platform === 'win32'
|
|
23
|
+
? path.join(os.homedir(), '.config', 'mcp-processes')
|
|
24
|
+
: path.join(os.homedir(), '.config', 'mcp-processes');
|
|
25
|
+
return path.join(configDir, 'tokens.json');
|
|
26
|
+
}
|
|
27
|
+
function loadTokenStore() {
|
|
28
|
+
const filePath = getTokenFilePath();
|
|
29
|
+
try {
|
|
30
|
+
const data = fs.readFileSync(filePath, 'utf-8');
|
|
31
|
+
const parsed = JSON.parse(data);
|
|
32
|
+
// Migrate single-token format to multi-environment store
|
|
33
|
+
if (parsed.refreshToken && parsed.authUrl) {
|
|
34
|
+
return { [parsed.authUrl]: parsed };
|
|
35
|
+
}
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function loadStoredTokens(authUrl) {
|
|
43
|
+
const store = loadTokenStore();
|
|
44
|
+
return store[authUrl] ?? null;
|
|
45
|
+
}
|
|
46
|
+
function saveTokens(tokens) {
|
|
47
|
+
const filePath = getTokenFilePath();
|
|
48
|
+
const dir = path.dirname(filePath);
|
|
49
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
50
|
+
// Load existing store and merge
|
|
51
|
+
const store = loadTokenStore();
|
|
52
|
+
store[tokens.authUrl] = tokens;
|
|
53
|
+
fs.writeFileSync(filePath, JSON.stringify(store, null, 2), { mode: 0o600 });
|
|
54
|
+
}
|
|
55
|
+
export function clearStoredTokens(authUrl) {
|
|
56
|
+
if (!authUrl) {
|
|
57
|
+
// Clear all tokens
|
|
58
|
+
const filePath = getTokenFilePath();
|
|
59
|
+
try {
|
|
60
|
+
fs.unlinkSync(filePath);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// File doesn't exist, that's fine
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Clear tokens for a specific auth URL
|
|
68
|
+
const store = loadTokenStore();
|
|
69
|
+
delete store[authUrl];
|
|
70
|
+
const filePath = getTokenFilePath();
|
|
71
|
+
const dir = path.dirname(filePath);
|
|
72
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
73
|
+
fs.writeFileSync(filePath, JSON.stringify(store, null, 2), { mode: 0o600 });
|
|
74
|
+
}
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// PKCE Helpers
|
|
77
|
+
// =============================================================================
|
|
78
|
+
function generateCodeVerifier() {
|
|
79
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
80
|
+
}
|
|
81
|
+
function generateCodeChallenge(verifier) {
|
|
82
|
+
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
83
|
+
}
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// Browser Helpers
|
|
86
|
+
// =============================================================================
|
|
87
|
+
/**
|
|
88
|
+
* Open a URL in the default browser.
|
|
89
|
+
* On Windows, cmd.exe interprets & as a command separator, so we write
|
|
90
|
+
* a temp HTML file with a redirect and open that instead.
|
|
91
|
+
*/
|
|
92
|
+
function openBrowser(url) {
|
|
93
|
+
try {
|
|
94
|
+
if (process.platform === 'win32') {
|
|
95
|
+
// Use PowerShell Start-Process — avoids cmd.exe's & interpretation issues with URLs
|
|
96
|
+
// Escape double quotes in URL to prevent PowerShell injection
|
|
97
|
+
const safeUrl = url.replace(/"/g, '`"');
|
|
98
|
+
spawn('powershell.exe', ['-NoProfile', '-Command', `Start-Process "${safeUrl}"`], {
|
|
99
|
+
stdio: 'ignore',
|
|
100
|
+
windowsHide: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else if (process.platform === 'darwin') {
|
|
104
|
+
spawn('open', [url], { stdio: 'ignore' });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
spawn('xdg-open', [url], { stdio: 'ignore' });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
console.error(`\nPlease open this URL in your browser to authenticate:\n${url}\n`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// HTML Page Helpers
|
|
116
|
+
// =============================================================================
|
|
117
|
+
function escapeHtml(str) {
|
|
118
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
119
|
+
}
|
|
120
|
+
function renderErrorPage(title, message, details) {
|
|
121
|
+
const timestamp = new Date().toISOString();
|
|
122
|
+
const reportBody = `Error: ${title}\nMessage: ${message}\nDetails: ${details}\nTimestamp: ${timestamp}\nPlatform: ${process.platform}\nNode: ${process.version}`;
|
|
123
|
+
return `<!DOCTYPE html>
|
|
124
|
+
<html><head><meta charset="utf-8"><title>MCP Processes — Error</title>
|
|
125
|
+
<link rel="icon" href="https://mag.ni/favicon.ico" />
|
|
126
|
+
<style>
|
|
127
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f8f9fa; color: #1a1a2e; }
|
|
128
|
+
.card { background: white; border-radius: 12px; padding: 48px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); text-align: center; max-width: 520px; }
|
|
129
|
+
.icon { font-size: 48px; margin-bottom: 16px; }
|
|
130
|
+
h1 { font-size: 22px; font-weight: 600; margin: 0 0 12px; color: #c0392b; }
|
|
131
|
+
p { color: #555; line-height: 1.6; margin: 0; }
|
|
132
|
+
.details { margin-top: 16px; padding: 12px 16px; background: #f1f3f5; border-radius: 8px; font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 12px; color: #666; text-align: left; word-break: break-all; }
|
|
133
|
+
.actions { margin-top: 24px; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
|
|
134
|
+
.btn { display: inline-block; padding: 10px 20px; border-radius: 8px; font-size: 14px; font-weight: 500; text-decoration: none; cursor: pointer; border: none; transition: background 0.2s; }
|
|
135
|
+
.btn-primary { background: #3b82f6; color: white; }
|
|
136
|
+
.btn-primary:hover { background: #2563eb; }
|
|
137
|
+
.btn-secondary { background: #e5e7eb; color: #374151; }
|
|
138
|
+
.btn-secondary:hover { background: #d1d5db; }
|
|
139
|
+
.hint { margin-top: 20px; font-size: 13px; color: #888; }
|
|
140
|
+
.copied { color: #16a34a; font-weight: 500; }
|
|
141
|
+
</style></head>
|
|
142
|
+
<body><div class="card">
|
|
143
|
+
<div class="icon">⚠</div>
|
|
144
|
+
<h1>${escapeHtml(title)}</h1>
|
|
145
|
+
<p>${escapeHtml(message)}</p>
|
|
146
|
+
<div class="details">
|
|
147
|
+
<div>${escapeHtml(details)}</div>
|
|
148
|
+
<div style="margin-top:4px;color:#999">${escapeHtml(timestamp)}</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="actions">
|
|
151
|
+
<button class="btn btn-primary" onclick="copyReport()">Copy Error Report</button>
|
|
152
|
+
<a class="btn btn-secondary" href="mailto:tech-success@magnifinance.com?subject=${encodeURIComponent('MCP Processes Auth Error: ' + title)}&body=${encodeURIComponent(reportBody)}">Email Report</a>
|
|
153
|
+
</div>
|
|
154
|
+
<p class="hint" id="hint">Copy the error report and send it to the development team so we can help.</p>
|
|
155
|
+
</div>
|
|
156
|
+
<script>
|
|
157
|
+
function copyReport() {
|
|
158
|
+
const report = ${JSON.stringify(reportBody)};
|
|
159
|
+
navigator.clipboard.writeText(report).then(function() {
|
|
160
|
+
document.getElementById('hint').innerHTML = '<span class="copied">Copied to clipboard!</span> Paste it in a message to the dev team.';
|
|
161
|
+
}).catch(function() {
|
|
162
|
+
// Fallback: select text in details box
|
|
163
|
+
var range = document.createRange();
|
|
164
|
+
range.selectNodeContents(document.querySelector('.details'));
|
|
165
|
+
var sel = window.getSelection();
|
|
166
|
+
sel.removeAllRanges();
|
|
167
|
+
sel.addRange(range);
|
|
168
|
+
document.getElementById('hint').textContent = 'Select and copy the error details above.';
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
</script>
|
|
172
|
+
</body></html>`;
|
|
173
|
+
}
|
|
174
|
+
// =============================================================================
|
|
175
|
+
// Authorization Code Flow
|
|
176
|
+
// =============================================================================
|
|
177
|
+
/**
|
|
178
|
+
* Start a local HTTP server and wait for the OAuth callback.
|
|
179
|
+
* Opens the browser to the authorization URL.
|
|
180
|
+
* Returns the authorization code.
|
|
181
|
+
*/
|
|
182
|
+
function waitForAuthorizationCode(authorizationUrl, port, expectedState) {
|
|
183
|
+
return new Promise((resolve, reject) => {
|
|
184
|
+
const server = http.createServer((req, res) => {
|
|
185
|
+
if (!req.url?.startsWith('/callback')) {
|
|
186
|
+
res.writeHead(404);
|
|
187
|
+
res.end('Not found');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
191
|
+
const code = url.searchParams.get('code');
|
|
192
|
+
const state = url.searchParams.get('state');
|
|
193
|
+
const error = url.searchParams.get('error');
|
|
194
|
+
const errorDescription = url.searchParams.get('error_description');
|
|
195
|
+
if (error) {
|
|
196
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
197
|
+
res.end(renderErrorPage('Authentication Failed', errorDescription || error, `OAuth error code: ${error}`));
|
|
198
|
+
server.close();
|
|
199
|
+
reject(new Error(`OAuth error: ${error} - ${errorDescription}`));
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (state !== expectedState) {
|
|
203
|
+
console.error(`State mismatch — expected: ${expectedState.substring(0, 8)}..., received: ${(state || 'null').substring(0, 8)}...`);
|
|
204
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
205
|
+
res.end(renderErrorPage('Invalid State', 'CSRF state mismatch. This can happen if you took too long to log in or opened multiple login tabs.', `Expected: ${expectedState.substring(0, 8)}..., Received: ${(state || 'null').substring(0, 8)}...`));
|
|
206
|
+
server.close();
|
|
207
|
+
reject(new Error('OAuth state mismatch'));
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (!code) {
|
|
211
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
212
|
+
res.end(renderErrorPage('Missing Authorization Code', 'The authorization server did not return an authorization code.', 'No authorization code was present in the callback response.'));
|
|
213
|
+
server.close();
|
|
214
|
+
reject(new Error('No authorization code received'));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
218
|
+
res.end(`<!DOCTYPE html>
|
|
219
|
+
<html><head><meta charset="utf-8"><title>MCP Processes — Authenticated</title>
|
|
220
|
+
<link rel="icon" href="https://mag.ni/favicon.ico" />
|
|
221
|
+
<style>
|
|
222
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f8f9fa; color: #1a1a2e; }
|
|
223
|
+
.card { background: white; border-radius: 12px; padding: 48px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); text-align: center; max-width: 440px; }
|
|
224
|
+
.icon { font-size: 48px; margin-bottom: 16px; }
|
|
225
|
+
h1 { font-size: 22px; font-weight: 600; margin: 0 0 12px; }
|
|
226
|
+
p { color: #555; line-height: 1.6; margin: 0; }
|
|
227
|
+
.hint { margin-top: 20px; font-size: 13px; color: #888; }
|
|
228
|
+
</style></head>
|
|
229
|
+
<body><div class="card">
|
|
230
|
+
<div class="icon">✓</div>
|
|
231
|
+
<h1>You're signed in to MCP Processes</h1>
|
|
232
|
+
<p>Your session token has been saved. The MCP server is now authenticated with your identity and tenant context.</p>
|
|
233
|
+
<p class="hint">You can close this tab and return to your terminal.</p>
|
|
234
|
+
</div></body></html>`);
|
|
235
|
+
server.close();
|
|
236
|
+
resolve(code);
|
|
237
|
+
});
|
|
238
|
+
server.listen(port, '127.0.0.1', () => {
|
|
239
|
+
// Open browser to authorization URL
|
|
240
|
+
openBrowser(authorizationUrl);
|
|
241
|
+
});
|
|
242
|
+
// Timeout after 2 minutes
|
|
243
|
+
setTimeout(() => {
|
|
244
|
+
server.close();
|
|
245
|
+
reject(new Error('Authentication timed out (2 minutes). Please try again.'));
|
|
246
|
+
}, 120000);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Exchange authorization code for tokens
|
|
251
|
+
*/
|
|
252
|
+
async function exchangeCodeForTokens(config, code, codeVerifier) {
|
|
253
|
+
const tokenUrl = `${config.authUrl}/connect/token`;
|
|
254
|
+
const params = {
|
|
255
|
+
grant_type: 'authorization_code',
|
|
256
|
+
client_id: config.clientId,
|
|
257
|
+
code,
|
|
258
|
+
redirect_uri: `http://localhost:${config.callbackPort}/callback`,
|
|
259
|
+
code_verifier: codeVerifier,
|
|
260
|
+
};
|
|
261
|
+
if (config.clientSecret) {
|
|
262
|
+
params.client_secret = config.clientSecret;
|
|
263
|
+
}
|
|
264
|
+
const body = new URLSearchParams(params);
|
|
265
|
+
const response = await fetch(tokenUrl, {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: {
|
|
268
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
269
|
+
'User-Agent': 'MCP-Processes/1.0',
|
|
270
|
+
},
|
|
271
|
+
body: body.toString(),
|
|
272
|
+
});
|
|
273
|
+
if (!response.ok) {
|
|
274
|
+
const errorText = await response.text();
|
|
275
|
+
throw new Error(`Token exchange failed (${response.status}): ${errorText}`);
|
|
276
|
+
}
|
|
277
|
+
return response.json();
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Use refresh token to get a new access token
|
|
281
|
+
*/
|
|
282
|
+
async function refreshAccessToken(config, refreshToken) {
|
|
283
|
+
const tokenUrl = `${config.authUrl}/connect/token`;
|
|
284
|
+
const params = {
|
|
285
|
+
grant_type: 'refresh_token',
|
|
286
|
+
client_id: config.clientId,
|
|
287
|
+
refresh_token: refreshToken,
|
|
288
|
+
};
|
|
289
|
+
if (config.clientSecret) {
|
|
290
|
+
params.client_secret = config.clientSecret;
|
|
291
|
+
}
|
|
292
|
+
const body = new URLSearchParams(params);
|
|
293
|
+
const response = await fetch(tokenUrl, {
|
|
294
|
+
method: 'POST',
|
|
295
|
+
headers: {
|
|
296
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
297
|
+
'User-Agent': 'MCP-Processes/1.0',
|
|
298
|
+
},
|
|
299
|
+
body: body.toString(),
|
|
300
|
+
});
|
|
301
|
+
if (!response.ok) {
|
|
302
|
+
const errorText = await response.text();
|
|
303
|
+
throw new Error(`Token refresh failed (${response.status}): ${errorText}`);
|
|
304
|
+
}
|
|
305
|
+
return response.json();
|
|
306
|
+
}
|
|
307
|
+
// =============================================================================
|
|
308
|
+
// OAuth Manager
|
|
309
|
+
// =============================================================================
|
|
310
|
+
export class OAuthManager {
|
|
311
|
+
config;
|
|
312
|
+
accessToken = null;
|
|
313
|
+
tokenExpiresAt = 0;
|
|
314
|
+
refreshToken = null;
|
|
315
|
+
constructor(config) {
|
|
316
|
+
this.config = config;
|
|
317
|
+
// Try to load stored refresh token for this environment
|
|
318
|
+
const stored = loadStoredTokens(config.authUrl);
|
|
319
|
+
if (stored && stored.clientId === config.clientId) {
|
|
320
|
+
this.refreshToken = stored.refreshToken;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Ensure we have a valid access token.
|
|
325
|
+
* If no token exists, triggers the browser-based login flow.
|
|
326
|
+
* If token is expired, uses refresh token to get a new one.
|
|
327
|
+
*/
|
|
328
|
+
async ensureAccessToken() {
|
|
329
|
+
// Token still valid (with 30s buffer)
|
|
330
|
+
if (this.accessToken && Date.now() < this.tokenExpiresAt - 30000) {
|
|
331
|
+
return this.accessToken;
|
|
332
|
+
}
|
|
333
|
+
// Try refresh token first
|
|
334
|
+
if (this.refreshToken) {
|
|
335
|
+
try {
|
|
336
|
+
const tokenResponse = await refreshAccessToken(this.config, this.refreshToken);
|
|
337
|
+
this.accessToken = tokenResponse.access_token;
|
|
338
|
+
this.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000;
|
|
339
|
+
// Update stored refresh token if a new one was issued
|
|
340
|
+
if (tokenResponse.refresh_token) {
|
|
341
|
+
this.refreshToken = tokenResponse.refresh_token;
|
|
342
|
+
saveTokens({
|
|
343
|
+
refreshToken: this.refreshToken,
|
|
344
|
+
authUrl: this.config.authUrl,
|
|
345
|
+
clientId: this.config.clientId,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
return this.accessToken;
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
// Refresh failed — fall through to interactive login
|
|
352
|
+
console.error('Token refresh failed, initiating login flow...');
|
|
353
|
+
this.refreshToken = null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// No valid token — start interactive login
|
|
357
|
+
return this.interactiveLogin();
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Start the interactive browser-based login flow
|
|
361
|
+
*/
|
|
362
|
+
async interactiveLogin() {
|
|
363
|
+
const codeVerifier = generateCodeVerifier();
|
|
364
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
365
|
+
const state = crypto.randomBytes(16).toString('hex');
|
|
366
|
+
const authParams = new URLSearchParams({
|
|
367
|
+
client_id: this.config.clientId,
|
|
368
|
+
redirect_uri: `http://localhost:${this.config.callbackPort}/callback`,
|
|
369
|
+
response_type: 'code',
|
|
370
|
+
scope: this.config.scopes.join(' '),
|
|
371
|
+
code_challenge: codeChallenge,
|
|
372
|
+
code_challenge_method: 'S256',
|
|
373
|
+
state,
|
|
374
|
+
});
|
|
375
|
+
const authorizationUrl = `${this.config.authUrl}/connect/authorize?${authParams.toString()}`;
|
|
376
|
+
console.error('Opening browser for authentication...');
|
|
377
|
+
// Wait for the user to authenticate and get the code
|
|
378
|
+
const code = await waitForAuthorizationCode(authorizationUrl, this.config.callbackPort, state);
|
|
379
|
+
// Exchange code for tokens
|
|
380
|
+
const tokenResponse = await exchangeCodeForTokens(this.config, code, codeVerifier);
|
|
381
|
+
this.accessToken = tokenResponse.access_token;
|
|
382
|
+
this.tokenExpiresAt = Date.now() + tokenResponse.expires_in * 1000;
|
|
383
|
+
// Store refresh token for future sessions
|
|
384
|
+
if (tokenResponse.refresh_token) {
|
|
385
|
+
this.refreshToken = tokenResponse.refresh_token;
|
|
386
|
+
saveTokens({
|
|
387
|
+
refreshToken: this.refreshToken,
|
|
388
|
+
authUrl: this.config.authUrl,
|
|
389
|
+
clientId: this.config.clientId,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
console.error('Authentication successful!');
|
|
393
|
+
return this.accessToken;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Get the current access token (may be null if not yet authenticated)
|
|
397
|
+
*/
|
|
398
|
+
getAccessToken() {
|
|
399
|
+
if (this.accessToken && Date.now() < this.tokenExpiresAt - 30000) {
|
|
400
|
+
return this.accessToken;
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Check if we have a stored refresh token (can authenticate without browser)
|
|
406
|
+
*/
|
|
407
|
+
hasStoredCredentials() {
|
|
408
|
+
return this.refreshToken !== null;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Clear all in-memory tokens and remove stored credentials for this environment.
|
|
412
|
+
* After calling this, the next ensureAccessToken() will trigger interactive login.
|
|
413
|
+
*/
|
|
414
|
+
clearCredentials() {
|
|
415
|
+
this.accessToken = null;
|
|
416
|
+
this.tokenExpiresAt = 0;
|
|
417
|
+
this.refreshToken = null;
|
|
418
|
+
clearStoredTokens(this.config.authUrl);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|