remote-access-mcp 1.0.7 → 2.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.fa.md +29 -70
- package/README.md +92 -65
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +352 -133
- package/dist/cli/main.js.map +1 -1
- package/dist/core/audit.d.ts +40 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +96 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/config.d.ts +37 -5
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +111 -34
- package/dist/core/config.js.map +1 -1
- package/dist/core/context.d.ts +23 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +32 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/crypto.d.ts +6 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +16 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/policy.d.ts +19 -0
- package/dist/core/policy.d.ts.map +1 -1
- package/dist/core/policy.js +57 -0
- package/dist/core/policy.js.map +1 -1
- package/dist/core/rate-limit.d.ts +14 -0
- package/dist/core/rate-limit.d.ts.map +1 -0
- package/dist/core/rate-limit.js +67 -0
- package/dist/core/rate-limit.js.map +1 -0
- package/dist/server/app.d.ts +18 -1
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/app.js +119 -25
- package/dist/server/app.js.map +1 -1
- package/dist/server/run.d.ts +5 -1
- package/dist/server/run.d.ts.map +1 -1
- package/dist/server/run.js +31 -8
- package/dist/server/run.js.map +1 -1
- package/dist/tools/filesystem.d.ts +3 -2
- package/dist/tools/filesystem.d.ts.map +1 -1
- package/dist/tools/filesystem.js +34 -23
- package/dist/tools/filesystem.js.map +1 -1
- package/dist/tools/git.d.ts +2 -2
- package/dist/tools/git.d.ts.map +1 -1
- package/dist/tools/git.js +33 -4
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/http.d.ts +2 -1
- package/dist/tools/http.d.ts.map +1 -1
- package/dist/tools/http.js +47 -5
- package/dist/tools/http.js.map +1 -1
- package/dist/tools/index.d.ts +6 -3
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +29 -9
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/logs.d.ts +4 -0
- package/dist/tools/logs.d.ts.map +1 -0
- package/dist/tools/logs.js +78 -0
- package/dist/tools/logs.js.map +1 -0
- package/dist/tools/packages.d.ts +4 -0
- package/dist/tools/packages.d.ts.map +1 -0
- package/dist/tools/packages.js +75 -0
- package/dist/tools/packages.js.map +1 -0
- package/dist/tools/planning.d.ts +4 -0
- package/dist/tools/planning.d.ts.map +1 -0
- package/dist/tools/planning.js +118 -0
- package/dist/tools/planning.js.map +1 -0
- package/dist/tools/policy.d.ts +7 -2
- package/dist/tools/policy.d.ts.map +1 -1
- package/dist/tools/policy.js +29 -18
- package/dist/tools/policy.js.map +1 -1
- package/dist/tools/project.d.ts +4 -0
- package/dist/tools/project.d.ts.map +1 -0
- package/dist/tools/project.js +141 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/schedule.d.ts +17 -0
- package/dist/tools/schedule.d.ts.map +1 -0
- package/dist/tools/schedule.js +113 -0
- package/dist/tools/schedule.js.map +1 -0
- package/dist/tools/security.d.ts +4 -0
- package/dist/tools/security.d.ts.map +1 -0
- package/dist/tools/security.js +103 -0
- package/dist/tools/security.js.map +1 -0
- package/dist/tools/services.d.ts +4 -0
- package/dist/tools/services.d.ts.map +1 -0
- package/dist/tools/services.js +56 -0
- package/dist/tools/services.js.map +1 -0
- package/dist/tools/shell.d.ts +2 -2
- package/dist/tools/shell.d.ts.map +1 -1
- package/dist/tools/shell.js +25 -10
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/sqlite.d.ts +2 -2
- package/dist/tools/sqlite.d.ts.map +1 -1
- package/dist/tools/sqlite.js +44 -9
- package/dist/tools/sqlite.js.map +1 -1
- package/dist/tools/system.d.ts +2 -1
- package/dist/tools/system.d.ts.map +1 -1
- package/dist/tools/system.js +6 -2
- package/dist/tools/system.js.map +1 -1
- package/dist/tools/web.d.ts +4 -0
- package/dist/tools/web.d.ts.map +1 -0
- package/dist/tools/web.js +77 -0
- package/dist/tools/web.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { assertToolPermitted } from '../core/policy.js';
|
|
7
|
+
const exec = promisify(execFile);
|
|
8
|
+
// Patterns that indicate a leaked credential. Deliberately broad —
|
|
9
|
+
// better a false positive the human reviews than a missed live key.
|
|
10
|
+
const SECRET_PATTERNS = [
|
|
11
|
+
['private key', /-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----/],
|
|
12
|
+
['aws access key', /AKIA[0-9A-Z]{16}/],
|
|
13
|
+
['github token (classic)', /gh[pousr]_[A-Za-z0-9]{20,}/],
|
|
14
|
+
['github fine-grained token', /github_pat_[A-Za-z0-9_]{20,}/],
|
|
15
|
+
['slack token', /xox[baprs]-[A-Za-z0-9-]{10,}/],
|
|
16
|
+
['telegram bot token', /\d{8,10}:AA[A-Za-z0-9_-]{30,}/],
|
|
17
|
+
['npm token', /npm_[A-Za-z0-9]{30,}/],
|
|
18
|
+
['google api key', /AIza[0-9A-Za-z_-]{35}/],
|
|
19
|
+
['generic bearer (long)', /bearer\s+[A-Za-z0-9._-]{40,}/i],
|
|
20
|
+
['password in connection string', /[a-z+]+:\/\/[^:\s]+:[^@\s]{8,}@/i],
|
|
21
|
+
];
|
|
22
|
+
const SKIP_DIRS = new Set(['.git', 'node_modules', '.venv', 'venv', '__pycache__', 'dist', 'build', '.next', 'coverage']);
|
|
23
|
+
const TEXT_EXT = /\.(txt|json|ya?ml|toml|ini|env|conf|cfg|js|jsx|ts|tsx|py|rb|go|rs|java|php|sh|bash|sql|md|html|css|xml)$/i;
|
|
24
|
+
export function registerSecurityTools(server, ctx) {
|
|
25
|
+
// ---- secret_scan -----------------------------------------------------------
|
|
26
|
+
server.registerTool('secret_scan', {
|
|
27
|
+
description: 'Scan a directory tree for leaked secrets (private keys, API tokens, passwords). Policy-checked.',
|
|
28
|
+
inputSchema: {
|
|
29
|
+
path: z.string().describe('Directory to scan'),
|
|
30
|
+
max_files: z.number().optional().default(2000).describe('File cap'),
|
|
31
|
+
},
|
|
32
|
+
}, async ({ path: dir, max_files }) => {
|
|
33
|
+
assertToolPermitted({
|
|
34
|
+
tool: 'secret_scan', scopes: ctx.token.scopes, readOnly: ctx.readOnly,
|
|
35
|
+
policy: { allowed_paths: ctx.token.allowed_paths, denied_paths: ctx.token.denied_paths, shell_enabled: ctx.token.shell_enabled },
|
|
36
|
+
target: dir,
|
|
37
|
+
});
|
|
38
|
+
const findings = [];
|
|
39
|
+
let scanned = 0;
|
|
40
|
+
const walk = async (d) => {
|
|
41
|
+
if (scanned >= max_files || findings.length >= 200)
|
|
42
|
+
return;
|
|
43
|
+
let entries;
|
|
44
|
+
try {
|
|
45
|
+
entries = await fsp.readdir(d, { withFileTypes: true });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
for (const e of entries) {
|
|
51
|
+
if (scanned >= max_files)
|
|
52
|
+
return;
|
|
53
|
+
if (e.name.startsWith('.') && e.name !== '.env')
|
|
54
|
+
continue;
|
|
55
|
+
const full = path.join(d, e.name);
|
|
56
|
+
if (e.isDirectory()) {
|
|
57
|
+
if (!SKIP_DIRS.has(e.name))
|
|
58
|
+
await walk(full);
|
|
59
|
+
}
|
|
60
|
+
else if (e.isFile() && TEXT_EXT.test(e.name)) {
|
|
61
|
+
scanned++;
|
|
62
|
+
let content;
|
|
63
|
+
try {
|
|
64
|
+
content = await fsp.readFile(full, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const lines = content.split('\n');
|
|
70
|
+
for (let i = 0; i < lines.length; i++) {
|
|
71
|
+
for (const [label, re] of SECRET_PATTERNS) {
|
|
72
|
+
if (re.test(lines[i])) {
|
|
73
|
+
// Mask the actual secret — we only report where it is
|
|
74
|
+
findings.push(`${full}:${i + 1} — possible ${label}`);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
await walk(dir);
|
|
83
|
+
const summary = findings.length
|
|
84
|
+
? `Found ${findings.length} potential secret(s) in ${scanned} files:\n${findings.join('\n')}`
|
|
85
|
+
: `Clean: no secrets found in ${scanned} files.`;
|
|
86
|
+
return { content: [{ type: 'text', text: summary }] };
|
|
87
|
+
});
|
|
88
|
+
// ---- port_scan_local ----------------------------------------------------------
|
|
89
|
+
server.registerTool('port_scan_local', {
|
|
90
|
+
description: 'List all listening TCP ports on this host with the owning process (ss -tlnp).',
|
|
91
|
+
inputSchema: {},
|
|
92
|
+
}, async () => {
|
|
93
|
+
assertToolPermitted({ tool: 'port_scan_local', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
94
|
+
try {
|
|
95
|
+
const { stdout } = await exec('ss', ['-tlnp']);
|
|
96
|
+
return { content: [{ type: 'text', text: stdout }] };
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
return { content: [{ type: 'text', text: e.message }], isError: true };
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/tools/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.d.ts","sourceRoot":"","sources":["../../src/tools/services.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAUtD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAgD9E"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { assertToolPermitted } from '../core/policy.js';
|
|
5
|
+
const exec = promisify(execFile);
|
|
6
|
+
const MAX = 40_000;
|
|
7
|
+
// Whitelist of actions — anything else would let the AI restart
|
|
8
|
+
// the gateway itself or drop into maintenance targets.
|
|
9
|
+
const SERVICE_ACTIONS = ['start', 'stop', 'restart', 'reload', 'status'];
|
|
10
|
+
const PROTECTED_UNITS = /^(remote-access-mcp|ssh|sshd|systemd|networkd|resolved|dbus|.*\.target)$/;
|
|
11
|
+
export function registerServiceTools(server, ctx) {
|
|
12
|
+
const policy = () => ({
|
|
13
|
+
allowed_paths: ctx.token.allowed_paths,
|
|
14
|
+
denied_paths: ctx.token.denied_paths,
|
|
15
|
+
shell_enabled: ctx.token.shell_enabled,
|
|
16
|
+
});
|
|
17
|
+
// ---- service_status -------------------------------------------------------
|
|
18
|
+
server.registerTool('service_status', {
|
|
19
|
+
description: 'Show whether a systemd unit is active/enabled (safe, read-only, any unit).',
|
|
20
|
+
inputSchema: { unit: z.string().regex(/^[A-Za-z0-9@._\\-]+$/) },
|
|
21
|
+
}, async ({ unit }) => {
|
|
22
|
+
assertToolPermitted({ tool: 'service_status', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
23
|
+
try {
|
|
24
|
+
const active = await exec('systemctl', ['is-active', unit]);
|
|
25
|
+
const enabled = await exec('systemctl', ['is-enabled', unit]);
|
|
26
|
+
return { content: [{ type: 'text', text: `active: ${active.stdout.trim()}\nenabled: ${enabled.stdout.trim()}` }] };
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
return { content: [{ type: 'text', text: e.stdout?.trim() || e.message }], isError: true };
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// ---- service_action ----------------------------------------------------------
|
|
33
|
+
server.registerTool('service_action', {
|
|
34
|
+
description: 'Start/stop/restart/reload a systemd service. Protected units (ssh, the gateway itself, targets) are refused.',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
unit: z.string().regex(/^[A-Za-z0-9@._\\-]+$/).describe('Service name, e.g. nginx.service'),
|
|
37
|
+
action: z.enum(SERVICE_ACTIONS).describe('start | stop | restart | reload | status'),
|
|
38
|
+
},
|
|
39
|
+
}, async ({ unit, action }) => {
|
|
40
|
+
assertToolPermitted({ tool: 'service_action', scopes: ctx.token.scopes, readOnly: ctx.readOnly, policy: policy() });
|
|
41
|
+
if (action === 'stop' && PROTECTED_UNITS.test(unit)) {
|
|
42
|
+
return { content: [{ type: 'text', text: `Refusing to ${action} protected unit ${unit}.` }], isError: true };
|
|
43
|
+
}
|
|
44
|
+
if (PROTECTED_UNITS.test(unit)) {
|
|
45
|
+
return { content: [{ type: 'text', text: `Refusing to touch protected unit ${unit}.` }], isError: true };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
const { stdout, stderr } = await exec('systemctl', [action, unit]);
|
|
49
|
+
return { content: [{ type: 'text', text: `${unit} ${action}: ok\n${stdout || stderr || ''}`.slice(0, MAX) }] };
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return { content: [{ type: 'text', text: [e.message, e.stdout, e.stderr].filter(Boolean).join('\n') }], isError: true };
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=services.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.js","sourceRoot":"","sources":["../../src/tools/services.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/shell.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import {
|
|
3
|
-
export declare function registerShellTools(server: McpServer,
|
|
2
|
+
import type { ToolContext } from '../core/context.js';
|
|
3
|
+
export declare function registerShellTools(server: McpServer, ctx: ToolContext): void;
|
|
4
4
|
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAOtD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAkF5E"}
|
package/dist/tools/shell.js
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
4
|
+
import { assertToolPermitted } from '../core/policy.js';
|
|
5
5
|
const exec = promisify(execFile);
|
|
6
6
|
const MAX_OUTPUT = 60_000;
|
|
7
|
-
|
|
7
|
+
// Shell injection surface: we run bash -c by design (the tool IS a shell).
|
|
8
|
+
// Guard rails: timeout, output cap, no TTY pty, cwd must exist.
|
|
9
|
+
export function registerShellTools(server, ctx) {
|
|
10
|
+
const policy = () => ({
|
|
11
|
+
allowed_paths: ctx.token.allowed_paths,
|
|
12
|
+
denied_paths: ctx.token.denied_paths,
|
|
13
|
+
shell_enabled: ctx.token.shell_enabled,
|
|
14
|
+
});
|
|
8
15
|
// ---- run_command -------------------------------------------------------
|
|
9
16
|
server.registerTool('run_command', {
|
|
10
|
-
description: 'Execute a shell command (
|
|
17
|
+
description: 'Execute a shell command (bash -c). Requires shell enabled for this token. 120s default timeout.',
|
|
11
18
|
inputSchema: {
|
|
12
19
|
command: z.string().describe('Command line to run'),
|
|
13
|
-
cwd: z.string().optional().describe('Working directory'),
|
|
20
|
+
cwd: z.string().optional().describe('Working directory (policy-checked)'),
|
|
14
21
|
timeout_ms: z.number().optional().default(120_000).describe('Timeout in ms (max 600000)'),
|
|
15
22
|
},
|
|
16
23
|
}, async ({ command, cwd, timeout_ms }) => {
|
|
17
|
-
if (!
|
|
18
|
-
return { content: [{ type: 'text', text: 'Shell execution is disabled. Enable with `ramcp policy shell on`.' }], isError: true };
|
|
24
|
+
if (!ctx.token.shell_enabled) {
|
|
25
|
+
return { content: [{ type: 'text', text: 'Shell execution is disabled for this token. Enable with `ramcp policy shell on` or `ramcp token add --shell`.' }], isError: true };
|
|
19
26
|
}
|
|
27
|
+
assertToolPermitted({ tool: 'run_command', scopes: ctx.token.scopes, readOnly: ctx.readOnly, policy: policy(), target: cwd });
|
|
20
28
|
const timeout = Math.min(timeout_ms, 600_000);
|
|
21
29
|
try {
|
|
22
|
-
const { stdout, stderr } = await exec('bash', ['-
|
|
30
|
+
const { stdout, stderr } = await exec('bash', ['-lc', command], {
|
|
23
31
|
cwd: cwd || undefined,
|
|
24
32
|
timeout,
|
|
25
33
|
maxBuffer: 10 * 1024 * 1024,
|
|
34
|
+
env: { ...process.env, TERM: 'dumb', LANG: 'C.UTF-8' },
|
|
26
35
|
});
|
|
27
36
|
let out = '';
|
|
28
37
|
if (stdout)
|
|
@@ -47,19 +56,25 @@ export function registerShellTools(server, policy) {
|
|
|
47
56
|
description: 'List running processes (pid, ppid, user, cpu%, mem%, command). Sorted by CPU.',
|
|
48
57
|
inputSchema: {},
|
|
49
58
|
}, async () => {
|
|
59
|
+
assertToolPermitted({ tool: 'process_list', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
50
60
|
const { stdout } = await exec('ps', ['axo', 'pid,ppid,user,%cpu,%mem,comm', '--sort=-%cpu']);
|
|
51
61
|
return { content: [{ type: 'text', text: stdout.slice(0, MAX_OUTPUT) }] };
|
|
52
62
|
});
|
|
53
63
|
// ---- kill_process ----------------------------------------------------------
|
|
54
64
|
server.registerTool('kill_process', {
|
|
55
|
-
description: 'Send a signal to a process.
|
|
65
|
+
description: 'Send a signal to a process. Requires shell enabled. Cannot kill the gateway itself.',
|
|
56
66
|
inputSchema: {
|
|
57
67
|
pid: z.number().describe('Process ID'),
|
|
58
68
|
signal: z.string().optional().default('SIGTERM').describe('Signal name, e.g. SIGTERM, SIGKILL'),
|
|
59
69
|
},
|
|
60
70
|
}, async ({ pid, signal }) => {
|
|
61
|
-
if (!
|
|
62
|
-
return { content: [{ type: 'text', text: 'Shell execution is disabled
|
|
71
|
+
if (!ctx.token.shell_enabled) {
|
|
72
|
+
return { content: [{ type: 'text', text: 'Shell execution is disabled for this token.' }], isError: true };
|
|
73
|
+
}
|
|
74
|
+
assertToolPermitted({ tool: 'kill_process', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
75
|
+
// Guardrail: the AI must not be able to kill the gateway or systemd.
|
|
76
|
+
if (pid === process.pid || pid === 1) {
|
|
77
|
+
return { content: [{ type: 'text', text: `Refusing to signal pid ${pid} (protected).` }], isError: true };
|
|
63
78
|
}
|
|
64
79
|
try {
|
|
65
80
|
process.kill(pid, signal);
|
package/dist/tools/shell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/sqlite.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import {
|
|
3
|
-
export declare function registerSqliteTools(server: McpServer,
|
|
2
|
+
import type { ToolContext } from '../core/context.js';
|
|
3
|
+
export declare function registerSqliteTools(server: McpServer, ctx: ToolContext): void;
|
|
4
4
|
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/tools/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAmBtD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAsE7E"}
|
package/dist/tools/sqlite.js
CHANGED
|
@@ -1,22 +1,54 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
|
|
3
|
+
import { assertToolPermitted } from '../core/policy.js';
|
|
4
|
+
// SQL safety: only one statement per call, and the verb allowlist stops
|
|
5
|
+
// multiple-statement smuggling (better-sqlite3's exec() would run them all).
|
|
6
|
+
const SQL_VERBS = /^(select|with|insert|update|delete|create|drop|alter|pragma|vacuum|analyze|begin|commit|rollback|explain|replace|index|reindex)\b/i;
|
|
7
|
+
function validateSql(sql) {
|
|
8
|
+
const trimmed = sql.trim();
|
|
9
|
+
if (!trimmed)
|
|
10
|
+
return 'empty SQL';
|
|
11
|
+
if (/;/.test(trimmed.slice(0, -1)) && !/pragma/i.test(trimmed))
|
|
12
|
+
return 'multiple statements not allowed';
|
|
13
|
+
const verbMatch = trimmed.match(/^[a-z]+/i);
|
|
14
|
+
if (!verbMatch)
|
|
15
|
+
return 'cannot parse SQL verb';
|
|
16
|
+
// WITH is legal CTE prefix; select/insert/etc. checked directly
|
|
17
|
+
if (!SQL_VERBS.test(verbMatch[0]))
|
|
18
|
+
return `SQL verb "${verbMatch[0]}" not allowed`;
|
|
19
|
+
// Attach/detach can pivot the DB file outside the policy sandbox
|
|
20
|
+
if (/\battach\b|\bdetach\b/i.test(trimmed))
|
|
21
|
+
return 'attach/detach not allowed';
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
export function registerSqliteTools(server, ctx) {
|
|
25
|
+
const policy = () => ({
|
|
26
|
+
allowed_paths: ctx.token.allowed_paths,
|
|
27
|
+
denied_paths: ctx.token.denied_paths,
|
|
28
|
+
shell_enabled: ctx.token.shell_enabled,
|
|
29
|
+
});
|
|
4
30
|
// ---- sqlite_query ------------------------------------------------------------------
|
|
5
31
|
server.registerTool('sqlite_query', {
|
|
6
|
-
description: 'Run SQL on a SQLite database file
|
|
32
|
+
description: 'Run one SQL statement on a SQLite database file. Verb allowlist, single-statement. Policy-checked.',
|
|
7
33
|
inputSchema: {
|
|
8
34
|
db_path: z.string().describe('Database file path'),
|
|
9
|
-
sql: z.string().describe('SQL statement
|
|
35
|
+
sql: z.string().describe('SQL statement'),
|
|
10
36
|
},
|
|
11
37
|
}, async ({ db_path, sql }) => {
|
|
12
|
-
|
|
38
|
+
assertToolPermitted({ tool: 'sqlite_query', scopes: ctx.token.scopes, readOnly: ctx.readOnly, policy: policy(), target: db_path });
|
|
39
|
+
const err = validateSql(sql);
|
|
40
|
+
if (err)
|
|
41
|
+
return { content: [{ type: 'text', text: `SQL rejected: ${err}` }], isError: true };
|
|
42
|
+
if (!fs.existsSync(db_path)) {
|
|
43
|
+
return { content: [{ type: 'text', text: `No such database: ${db_path}` }], isError: true };
|
|
44
|
+
}
|
|
13
45
|
try {
|
|
14
46
|
const { default: Database } = await import('better-sqlite3');
|
|
15
|
-
const db = new Database(db_path, {
|
|
47
|
+
const db = new Database(db_path, { fileMustExist: true });
|
|
16
48
|
try {
|
|
17
49
|
const trimmed = sql.trim().toLowerCase();
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
50
|
+
const readOnlyVerb = trimmed.startsWith('select') || trimmed.startsWith('with') || trimmed.startsWith('pragma') || trimmed.startsWith('explain');
|
|
51
|
+
if (readOnlyVerb) {
|
|
20
52
|
const rows = db.prepare(sql).all();
|
|
21
53
|
return { content: [{ type: 'text', text: JSON.stringify(rows, null, 2).slice(0, 60_000) || '[]' }] };
|
|
22
54
|
}
|
|
@@ -36,7 +68,10 @@ export function registerSqliteTools(server, policy) {
|
|
|
36
68
|
description: 'List tables and their CREATE statements for a SQLite database. Policy-checked.',
|
|
37
69
|
inputSchema: { db_path: z.string().describe('Database file path') },
|
|
38
70
|
}, async ({ db_path }) => {
|
|
39
|
-
|
|
71
|
+
assertToolPermitted({ tool: 'sqlite_schema', scopes: ctx.token.scopes, readOnly: ctx.readOnly, policy: policy(), target: db_path });
|
|
72
|
+
if (!fs.existsSync(db_path)) {
|
|
73
|
+
return { content: [{ type: 'text', text: `No such database: ${db_path}` }], isError: true };
|
|
74
|
+
}
|
|
40
75
|
try {
|
|
41
76
|
const { default: Database } = await import('better-sqlite3');
|
|
42
77
|
const db = new Database(db_path, { readonly: true, fileMustExist: true });
|
package/dist/tools/sqlite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/tools/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/tools/sqlite.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/system.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
|
|
2
|
+
import type { ToolContext } from '../core/context.js';
|
|
3
|
+
export declare function registerSystemTools(server: McpServer, ctx: ToolContext): void;
|
|
3
4
|
//# sourceMappingURL=system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../src/tools/system.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../src/tools/system.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CA0D7E"}
|
package/dist/tools/system.js
CHANGED
|
@@ -2,13 +2,15 @@ import os from 'node:os';
|
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { assertToolPermitted } from '../core/policy.js';
|
|
5
6
|
const exec = promisify(execFile);
|
|
6
|
-
export function registerSystemTools(server) {
|
|
7
|
+
export function registerSystemTools(server, ctx) {
|
|
7
8
|
// ---- system_info ---------------------------------------------------------
|
|
8
9
|
server.registerTool('system_info', {
|
|
9
10
|
description: 'Host, platform, CPU, memory, uptime, and Node.js version of the server.',
|
|
10
11
|
inputSchema: {},
|
|
11
12
|
}, async () => {
|
|
13
|
+
assertToolPermitted({ tool: 'system_info', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
12
14
|
const cpus = os.cpus();
|
|
13
15
|
const out = [
|
|
14
16
|
`hostname: ${os.hostname()}`,
|
|
@@ -24,9 +26,10 @@ export function registerSystemTools(server) {
|
|
|
24
26
|
});
|
|
25
27
|
// ---- disk_usage ------------------------------------------------------------
|
|
26
28
|
server.registerTool('disk_usage', {
|
|
27
|
-
description: 'Filesystem sizes and usage. Human-readable df output
|
|
29
|
+
description: 'Filesystem sizes and usage. Human-readable df output.',
|
|
28
30
|
inputSchema: { path: z.string().optional().describe('Restrict to a mount point') },
|
|
29
31
|
}, async ({ path }) => {
|
|
32
|
+
assertToolPermitted({ tool: 'disk_usage', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
30
33
|
const args = ['-h'];
|
|
31
34
|
if (path)
|
|
32
35
|
args.push(path);
|
|
@@ -43,6 +46,7 @@ export function registerSystemTools(server) {
|
|
|
43
46
|
description: 'List network interfaces with addresses. Loopback included.',
|
|
44
47
|
inputSchema: {},
|
|
45
48
|
}, async () => {
|
|
49
|
+
assertToolPermitted({ tool: 'network_interfaces', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
46
50
|
const ifaces = os.networkInterfaces();
|
|
47
51
|
const lines = [];
|
|
48
52
|
for (const [name, addrs] of Object.entries(ifaces)) {
|
package/dist/tools/system.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/tools/system.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/tools/system.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/tools/web.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAqCtD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAmC1E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { assertToolPermitted } from '../core/policy.js';
|
|
3
|
+
const MAX_BODY = 512 * 1024;
|
|
4
|
+
// SSRF guard: refuse requests to loopback/link-local/metadata addresses
|
|
5
|
+
// unless explicitly allowed. The gateway itself listens on loopback —
|
|
6
|
+
// an AI must not be able to bypass auth by fetching its own endpoint
|
|
7
|
+
// from inside, or hit cloud metadata services.
|
|
8
|
+
import net from 'node:net';
|
|
9
|
+
function isBlockedHost(hostname) {
|
|
10
|
+
// IPv4 literals
|
|
11
|
+
if (net.isIPv4(hostname)) {
|
|
12
|
+
const parts = hostname.split('.').map(Number);
|
|
13
|
+
const [a, b] = parts;
|
|
14
|
+
if (a === 127 || a === 10)
|
|
15
|
+
return true;
|
|
16
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
17
|
+
return true;
|
|
18
|
+
if (a === 192 && b === 168)
|
|
19
|
+
return true;
|
|
20
|
+
if (a === 169 && b === 254)
|
|
21
|
+
return true; // link-local incl. cloud metadata
|
|
22
|
+
if (a === 0)
|
|
23
|
+
return true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
// IPv6 literals
|
|
27
|
+
if (net.isIPv6(hostname)) {
|
|
28
|
+
const h = hostname.toLowerCase();
|
|
29
|
+
if (h === '::1' || h === '::' || h.startsWith('fe80') || h.startsWith('fc') || h.startsWith('fd'))
|
|
30
|
+
return true;
|
|
31
|
+
// IPv4-mapped
|
|
32
|
+
const m = h.match(/::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
33
|
+
if (m)
|
|
34
|
+
return isBlockedHost(m[1]);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
// Hostnames that resolve to private ranges are checked at connect time
|
|
38
|
+
// by fetch itself — but block the obvious ones textually:
|
|
39
|
+
const blockedNames = ['metadata.google.internal', 'instance-data', 'localhost'];
|
|
40
|
+
return blockedNames.some(n => hostname === n || hostname.endsWith('.' + n));
|
|
41
|
+
}
|
|
42
|
+
export function registerWebTools(server, ctx) {
|
|
43
|
+
// ---- web_fetch ---------------------------------------------------------------
|
|
44
|
+
server.registerTool('web_fetch', {
|
|
45
|
+
description: 'Fetch a URL and return the response body (text, truncated). SSRF-guarded: private/metadata ranges are refused.',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
url: z.string().url().describe('Public URL to fetch'),
|
|
48
|
+
max_bytes: z.number().optional().default(100_000).describe('Max body bytes'),
|
|
49
|
+
},
|
|
50
|
+
}, async ({ url, max_bytes }) => {
|
|
51
|
+
assertToolPermitted({ tool: 'web_fetch', scopes: ctx.token.scopes, readOnly: ctx.readOnly });
|
|
52
|
+
const parsed = new URL(url);
|
|
53
|
+
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
54
|
+
return { content: [{ type: 'text', text: `Protocol ${parsed.protocol} not allowed.` }], isError: true };
|
|
55
|
+
}
|
|
56
|
+
if (isBlockedHost(parsed.hostname)) {
|
|
57
|
+
return { content: [{ type: 'text', text: `Refusing to fetch internal/private host ${parsed.hostname}.` }], isError: true };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => controller.abort(), 20_000);
|
|
62
|
+
const resp = await fetch(url, {
|
|
63
|
+
redirect: 'follow',
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
headers: { 'User-Agent': 'remote-access-mcp/2.0 (+https://github.com/AmirAliManzar/remote-access-mcp)' },
|
|
66
|
+
});
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
const text = (await resp.text()).slice(0, Math.min(max_bytes, MAX_BODY));
|
|
69
|
+
const head = `status: ${resp.status} ${resp.statusText}\ncontent-type: ${resp.headers.get('content-type') || '?'}\n\n`;
|
|
70
|
+
return { content: [{ type: 'text', text: head + text }] };
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
return { content: [{ type: 'text', text: `fetch failed: ${e.message}` }], isError: true };
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/tools/web.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "remote-access-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Turn any Linux server into an AI-agent-accessible machine via MCP. ChatGPT, Claude, and Grok connect over HTTPS and control files, shell, git, and more — securely and with zero Python dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|