dashley-mcp-framework 1.0.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/README.md +70 -0
- package/dist/api/client.d.ts +2 -0
- package/dist/api/client.js +35 -0
- package/dist/api/endpoints.d.ts +7 -0
- package/dist/api/endpoints.js +8 -0
- package/dist/api/types.d.ts +9 -0
- package/dist/api/types.js +1 -0
- package/dist/auth/login.d.ts +2 -0
- package/dist/auth/login.js +112 -0
- package/dist/auth/refresh.d.ts +2 -0
- package/dist/auth/refresh.js +29 -0
- package/dist/auth/session.d.ts +5 -0
- package/dist/auth/session.js +50 -0
- package/dist/auth/types.d.ts +14 -0
- package/dist/auth/types.js +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/login.d.ts +1 -0
- package/dist/cli/login.js +17 -0
- package/dist/cli/status.d.ts +1 -0
- package/dist/cli/status.js +20 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +71 -0
- package/dist/mcp/tools/example-tool.d.ts +15 -0
- package/dist/mcp/tools/example-tool.js +17 -0
- package/dist/mcp/tools/index.d.ts +14 -0
- package/dist/mcp/tools/index.js +4 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# mcp-chaos-auth
|
|
2
|
+
|
|
3
|
+
MCP server with authenticated access to Chaos Auth backend.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g mcp-chaos-auth
|
|
9
|
+
rm -rf /Users/joshua.icuss/mcp-servers/claude-mcp-minimal; mkdir /Users/joshua.icuss/mcp-servers/claude-mcp-minimal
|
|
10
|
+
cp -R /Users/joshua.icuss/Documents/claude-mcp-minimal/* /Users/joshua.icuss/mcp-servers/claude-mcp-minimal
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Link it locally for development - If you want to test the CLI tool while developing, you can link it:
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
npm install
|
|
17
|
+
npm run build
|
|
18
|
+
npm link
|
|
19
|
+
|
|
20
|
+
/opt/homebrew/bin/npx
|
|
21
|
+
|
|
22
|
+
tasty-mcp-minimal
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
node /Users/joshua.icuss/mcp-servers/claude-mcp-minimal/dist/index.js
|
|
27
|
+
npm login
|
|
28
|
+
|
|
29
|
+
Update package.json:
|
|
30
|
+
File operations writeTFile operations writeResultDone
|
|
31
|
+
|
|
32
|
+
npm run build
|
|
33
|
+
npm publish
|
|
34
|
+
|
|
35
|
+
npm run build
|
|
36
|
+
npm unlink -g
|
|
37
|
+
npm link
|
|
38
|
+
|
|
39
|
+
mcp-chaos-auth-server
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
npm install mcp-chaos-auth
|
|
43
|
+
|
|
44
|
+
npm mcp-chaos-auth-server
|
|
45
|
+
|
|
46
|
+
**Option 1 - Global install:**
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g mcp-chaos-auth
|
|
49
|
+
mcp-chaos-auth-server
|
|
50
|
+
```
|
|
51
|
+
```bash
|
|
52
|
+
mcp-chaos-auth login
|
|
53
|
+
mcp-chaos-auth status
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Shows session expiry and permission count. Does not call backend.
|
|
57
|
+
|
|
58
|
+
### Claude Desktop
|
|
59
|
+
|
|
60
|
+
Add to `claude_desktop_config.json`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"chaos-auth": {
|
|
66
|
+
"command": "tasty-mcp-server"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { readSession, deleteSession } from '../auth/session.js';
|
|
3
|
+
import { refreshIfNeeded } from '../auth/refresh.js';
|
|
4
|
+
export function createClient(apiUrl) {
|
|
5
|
+
const client = axios.create({
|
|
6
|
+
baseURL: apiUrl,
|
|
7
|
+
});
|
|
8
|
+
client.interceptors.request.use(async (config) => {
|
|
9
|
+
const session = readSession();
|
|
10
|
+
if (!session) {
|
|
11
|
+
throw new Error('Not logged in. Run: mcp-chaos-auth login');
|
|
12
|
+
}
|
|
13
|
+
const refreshedSession = await refreshIfNeeded(session, apiUrl);
|
|
14
|
+
if (!refreshedSession) {
|
|
15
|
+
deleteSession();
|
|
16
|
+
throw new Error('Session expired. Run: mcp-chaos-auth login');
|
|
17
|
+
}
|
|
18
|
+
config.headers.Authorization = `Bearer ${refreshedSession.token}`;
|
|
19
|
+
return config;
|
|
20
|
+
});
|
|
21
|
+
client.interceptors.response.use((response) => response, (error) => {
|
|
22
|
+
if (error.response?.status === 401) {
|
|
23
|
+
deleteSession();
|
|
24
|
+
throw new Error('Authentication failed. Please login again.');
|
|
25
|
+
}
|
|
26
|
+
if (error.response?.status === 403) {
|
|
27
|
+
throw new Error(`Insufficient permissions: ${error.response.data?.detail || 'Access denied'}`);
|
|
28
|
+
}
|
|
29
|
+
if (error.response?.status >= 500) {
|
|
30
|
+
throw new Error(`Server error: ${error.response.status}`);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
});
|
|
34
|
+
return client;
|
|
35
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
export declare function validateSession(client: AxiosInstance): Promise<{
|
|
3
|
+
username: string;
|
|
4
|
+
roles: string[];
|
|
5
|
+
permissions: string[];
|
|
6
|
+
}>;
|
|
7
|
+
export declare function getUserPermissions(client: AxiosInstance): Promise<string[]>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export async function validateSession(client) {
|
|
2
|
+
const response = await client.get('/auth/validate');
|
|
3
|
+
return response.data;
|
|
4
|
+
}
|
|
5
|
+
export async function getUserPermissions(client) {
|
|
6
|
+
const response = await client.get('/users/me/permissions');
|
|
7
|
+
return response.data;
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
async function prompt(question) {
|
|
4
|
+
const rl = readline.createInterface({
|
|
5
|
+
input: process.stdin,
|
|
6
|
+
output: process.stdout,
|
|
7
|
+
});
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
rl.question(question, (answer) => {
|
|
10
|
+
rl.close();
|
|
11
|
+
resolve(answer);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function promptSecret(question) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
const rl = readline.createInterface({
|
|
18
|
+
input: process.stdin,
|
|
19
|
+
output: process.stdout,
|
|
20
|
+
terminal: false,
|
|
21
|
+
});
|
|
22
|
+
const stdin = process.stdin;
|
|
23
|
+
stdin.setRawMode(true);
|
|
24
|
+
stdin.resume();
|
|
25
|
+
stdin.setEncoding('utf8');
|
|
26
|
+
process.stdout.write(question);
|
|
27
|
+
let password = '';
|
|
28
|
+
const onData = (char) => {
|
|
29
|
+
switch (char) {
|
|
30
|
+
case '\n':
|
|
31
|
+
case '\r':
|
|
32
|
+
case '\u0004':
|
|
33
|
+
stdin.setRawMode(false);
|
|
34
|
+
stdin.pause();
|
|
35
|
+
stdin.removeListener('data', onData);
|
|
36
|
+
process.stdout.write('\n');
|
|
37
|
+
resolve(password);
|
|
38
|
+
break;
|
|
39
|
+
case '\u0003':
|
|
40
|
+
stdin.setRawMode(false);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
break;
|
|
43
|
+
case '\u007f':
|
|
44
|
+
if (password.length > 0) {
|
|
45
|
+
password = password.slice(0, -1);
|
|
46
|
+
process.stdout.write('\b \b');
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
if (char.charCodeAt(0) >= 32) {
|
|
51
|
+
password += char;
|
|
52
|
+
process.stdout.write('*');
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
stdin.on('data', onData);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export async function login(apiUrl) {
|
|
61
|
+
const username = await prompt('Username: ');
|
|
62
|
+
const password = await promptSecret('Password: ');
|
|
63
|
+
try {
|
|
64
|
+
const loginResponse = await axios.post(`${apiUrl}/auth/login`, {
|
|
65
|
+
username,
|
|
66
|
+
password,
|
|
67
|
+
});
|
|
68
|
+
let accessToken = loginResponse.data.access_token;
|
|
69
|
+
let permissions = loginResponse.data.permissions || [];
|
|
70
|
+
if (loginResponse.data.mfa_required && loginResponse.data.challenge_token) {
|
|
71
|
+
const challengeToken = loginResponse.data.challenge_token;
|
|
72
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
73
|
+
const mfaCode = await prompt('MFA Code: ');
|
|
74
|
+
try {
|
|
75
|
+
const mfaResponse = await axios.post(`${apiUrl}/auth/mfa/challenge`, {
|
|
76
|
+
challenge_token: challengeToken,
|
|
77
|
+
code: mfaCode,
|
|
78
|
+
});
|
|
79
|
+
accessToken = mfaResponse.data.access_token;
|
|
80
|
+
permissions = mfaResponse.data.permissions || [];
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (attempt === 3) {
|
|
85
|
+
throw new Error('MFA verification failed after 3 attempts');
|
|
86
|
+
}
|
|
87
|
+
console.error('Invalid MFA code, try again');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const validateResponse = await axios.get(`${apiUrl}/auth/validate`, {
|
|
92
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
93
|
+
});
|
|
94
|
+
const validatedPermissions = validateResponse.data.permissions || permissions;
|
|
95
|
+
const now = new Date();
|
|
96
|
+
return {
|
|
97
|
+
token: accessToken,
|
|
98
|
+
expiresAt: new Date(now.getTime() + loginResponse.data.expires_in * 1000),
|
|
99
|
+
loginAt: now,
|
|
100
|
+
permissions: validatedPermissions,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error.response?.status === 401) {
|
|
105
|
+
throw new Error('Invalid credentials');
|
|
106
|
+
}
|
|
107
|
+
if (!error.response) {
|
|
108
|
+
throw new Error('Cannot reach auth server');
|
|
109
|
+
}
|
|
110
|
+
throw new Error(error.response?.data?.detail || error.message);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { isExpired } from './session.js';
|
|
3
|
+
export async function refreshIfNeeded(session, apiUrl) {
|
|
4
|
+
if (isExpired(session)) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const now = new Date();
|
|
8
|
+
const minutesUntilExpiry = (session.expiresAt.getTime() - now.getTime()) / (1000 * 60);
|
|
9
|
+
if (minutesUntilExpiry > 5) {
|
|
10
|
+
return session;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
const response = await axios.post(`${apiUrl}/auth/token/refresh`, {}, {
|
|
14
|
+
headers: { Authorization: `Bearer ${session.token}` },
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
token: response.data.access_token,
|
|
18
|
+
expiresAt: session.expiresAt,
|
|
19
|
+
loginAt: session.loginAt,
|
|
20
|
+
permissions: session.permissions,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error.response?.status === 401) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return session;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Session } from './types.js';
|
|
2
|
+
export declare function readSession(): Session | null;
|
|
3
|
+
export declare function writeSession(token: string, expiresIn: number, permissions: string[]): void;
|
|
4
|
+
export declare function deleteSession(): void;
|
|
5
|
+
export declare function isExpired(session: Session): boolean;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.config', 'mcp-chaos-auth');
|
|
5
|
+
const SESSION_FILE = path.join(CONFIG_DIR, 'session.json');
|
|
6
|
+
export function readSession() {
|
|
7
|
+
try {
|
|
8
|
+
const data = fs.readFileSync(SESSION_FILE, 'utf-8');
|
|
9
|
+
const parsed = JSON.parse(data);
|
|
10
|
+
const session = {
|
|
11
|
+
...parsed,
|
|
12
|
+
expiresAt: new Date(parsed.expiresAt),
|
|
13
|
+
loginAt: new Date(parsed.loginAt),
|
|
14
|
+
};
|
|
15
|
+
if (isExpired(session)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return session;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function writeSession(token, expiresIn, permissions) {
|
|
25
|
+
const now = new Date();
|
|
26
|
+
const session = {
|
|
27
|
+
token,
|
|
28
|
+
expiresAt: new Date(now.getTime() + expiresIn * 1000),
|
|
29
|
+
loginAt: now,
|
|
30
|
+
permissions,
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
34
|
+
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2), { mode: 0o600 });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error(`Failed to write session: ${error}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function deleteSession() {
|
|
41
|
+
try {
|
|
42
|
+
fs.unlinkSync(SESSION_FILE);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Ignore errors
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function isExpired(session) {
|
|
49
|
+
return new Date() > session.expiresAt;
|
|
50
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Session {
|
|
2
|
+
token: string;
|
|
3
|
+
expiresAt: Date;
|
|
4
|
+
loginAt: Date;
|
|
5
|
+
permissions: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface LoginResponse {
|
|
8
|
+
access_token: string;
|
|
9
|
+
token_type: string;
|
|
10
|
+
expires_in: number;
|
|
11
|
+
permissions?: string[];
|
|
12
|
+
mfa_required?: boolean;
|
|
13
|
+
challenge_token?: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { loginCommand } from './login.js';
|
|
2
|
+
import { statusCommand } from './status.js';
|
|
3
|
+
import { API_URL } from '../config.js';
|
|
4
|
+
const command = process.argv[2];
|
|
5
|
+
if (command === 'login') {
|
|
6
|
+
loginCommand(API_URL);
|
|
7
|
+
}
|
|
8
|
+
else if (command === 'status') {
|
|
9
|
+
statusCommand();
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
console.error('Usage: mcp-chaos-auth <login|status>');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loginCommand(apiUrl: string): Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { login } from '../auth/login.js';
|
|
2
|
+
import { writeSession } from '../auth/session.js';
|
|
3
|
+
export async function loginCommand(apiUrl) {
|
|
4
|
+
try {
|
|
5
|
+
const session = await login(apiUrl);
|
|
6
|
+
const expiresIn = Math.floor((session.expiresAt.getTime() - session.loginAt.getTime()) / 1000);
|
|
7
|
+
writeSession(session.token, expiresIn, session.permissions);
|
|
8
|
+
console.log('✓ Logged in successfully');
|
|
9
|
+
console.log(`Session valid until: ${session.expiresAt.toLocaleString()}`);
|
|
10
|
+
console.log("Run 'mcp-chaos-auth status' to check session");
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.error(`Error: ${error.message}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function statusCommand(): Promise<void>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readSession } from '../auth/session.js';
|
|
2
|
+
export async function statusCommand() {
|
|
3
|
+
const session = readSession();
|
|
4
|
+
if (!session) {
|
|
5
|
+
console.log('Not logged in');
|
|
6
|
+
process.exit(0);
|
|
7
|
+
}
|
|
8
|
+
const now = new Date();
|
|
9
|
+
if (now > session.expiresAt) {
|
|
10
|
+
console.log('Session expired');
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
const timeRemaining = Math.floor((session.expiresAt.getTime() - now.getTime()) / (1000 * 60));
|
|
14
|
+
const hours = Math.floor(timeRemaining / 60);
|
|
15
|
+
const minutes = timeRemaining % 60;
|
|
16
|
+
console.log(`Session expires: ${session.expiresAt.toLocaleString()}`);
|
|
17
|
+
console.log(`Time remaining: ${hours}h ${minutes}m`);
|
|
18
|
+
console.log(`Permissions: ${session.permissions.length} loaded`);
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const API_URL: string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const API_URL = process.env.AUTH_API_URL || 'https://authentication.stable.tastydata.io/api';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startServer(apiUrl: string): Promise<void>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import { readSession } from '../auth/session.js';
|
|
5
|
+
import { createClient } from '../api/client.js';
|
|
6
|
+
import { validateSession } from '../api/endpoints.js';
|
|
7
|
+
import { tools } from './tools/index.js';
|
|
8
|
+
export async function startServer(apiUrl) {
|
|
9
|
+
const session = readSession();
|
|
10
|
+
if (!session) {
|
|
11
|
+
console.error('No session found. Run: mcp-chaos-auth login');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const client = createClient(apiUrl);
|
|
15
|
+
try {
|
|
16
|
+
await validateSession(client);
|
|
17
|
+
console.error('MCP server starting');
|
|
18
|
+
console.error(`Session valid until ${session.expiresAt.toISOString()}`);
|
|
19
|
+
console.error(`Loaded ${tools.length} tools`);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error(`Cannot reach auth server: ${error.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const server = new Server({
|
|
26
|
+
name: 'mcp-chaos-auth',
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
}, {
|
|
29
|
+
capabilities: {
|
|
30
|
+
tools: {},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
34
|
+
tools: tools.map((tool) => ({
|
|
35
|
+
name: tool.name,
|
|
36
|
+
description: tool.description,
|
|
37
|
+
inputSchema: tool.inputSchema,
|
|
38
|
+
})),
|
|
39
|
+
}));
|
|
40
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
41
|
+
const tool = tools.find((t) => t.name === request.params.name);
|
|
42
|
+
if (!tool) {
|
|
43
|
+
throw new Error(`Tool not found: ${request.params.name}`);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const result = await tool.handler(client);
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: JSON.stringify(result, null, 2),
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: `Error: ${error.message}`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
isError: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const transport = new StdioServerTransport();
|
|
69
|
+
await server.connect(transport);
|
|
70
|
+
console.error('MCP server ready');
|
|
71
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
export declare const exampleTool: {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: string;
|
|
7
|
+
properties: {};
|
|
8
|
+
required: never[];
|
|
9
|
+
};
|
|
10
|
+
handler: (client: AxiosInstance) => Promise<{
|
|
11
|
+
username: any;
|
|
12
|
+
roles: any;
|
|
13
|
+
permissions: any;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const exampleTool = {
|
|
2
|
+
name: "chaos_auth_whoami",
|
|
3
|
+
description: "Get current authenticated user info",
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {},
|
|
7
|
+
required: [],
|
|
8
|
+
},
|
|
9
|
+
handler: async (client) => {
|
|
10
|
+
const response = await client.get("/users/me");
|
|
11
|
+
return {
|
|
12
|
+
username: response.data.username,
|
|
13
|
+
roles: response.data.roles,
|
|
14
|
+
permissions: response.data.permissions,
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const tools: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: string;
|
|
6
|
+
properties: {};
|
|
7
|
+
required: never[];
|
|
8
|
+
};
|
|
9
|
+
handler: (client: import("axios").AxiosInstance) => Promise<{
|
|
10
|
+
username: any;
|
|
11
|
+
roles: any;
|
|
12
|
+
permissions: any;
|
|
13
|
+
}>;
|
|
14
|
+
}[];
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashley-mcp-framework",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "MCP server with authenticated access to Chaos Auth backend",
|
|
5
|
+
"author": "Joshua Icuss",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/yourusername/mcp-chaos-auth"
|
|
10
|
+
},
|
|
11
|
+
"keywords": ["mcp", "authentication", "chaos-auth"],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"bin"
|
|
16
|
+
],
|
|
17
|
+
"bin": {
|
|
18
|
+
"tasty-auth": "./bin/tasty-auth",
|
|
19
|
+
"tasty-mcp-server": "./bin/tasty-mcp-server"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"dev": "tsx src/index.ts",
|
|
24
|
+
"login": "tsx src/cli/index.ts login"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
28
|
+
"axios": "^1.7.9"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^22.10.5",
|
|
32
|
+
"tsx": "^4.19.2",
|
|
33
|
+
"typescript": "^5.7.2"
|
|
34
|
+
}
|
|
35
|
+
}
|