@majkapp/majk-chat-mcp 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 +854 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +287 -0
- package/dist/client/mcp-client.d.ts +22 -0
- package/dist/client/mcp-client.d.ts.map +1 -0
- package/dist/client/mcp-client.js +136 -0
- package/dist/config/parser.d.ts +14 -0
- package/dist/config/parser.d.ts.map +1 -0
- package/dist/config/parser.js +142 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/plugin.d.ts +24 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +165 -0
- package/dist/tools/dynamic-executor.d.ts +32 -0
- package/dist/tools/dynamic-executor.d.ts.map +1 -0
- package/dist/tools/dynamic-executor.js +257 -0
- package/dist/tools/mcp-tool-executor.d.ts +16 -0
- package/dist/tools/mcp-tool-executor.d.ts.map +1 -0
- package/dist/tools/mcp-tool-executor.js +93 -0
- package/dist/tools/permission-handler.d.ts +18 -0
- package/dist/tools/permission-handler.d.ts.map +1 -0
- package/dist/tools/permission-handler.js +91 -0
- package/dist/tools/tool-filter.d.ts +20 -0
- package/dist/tools/tool-filter.d.ts.map +1 -0
- package/dist/tools/tool-filter.js +93 -0
- package/dist/types/config.d.ts +56 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/package.json +54 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MCPPermissionHandler = void 0;
|
|
4
|
+
const parser_1 = require("../config/parser");
|
|
5
|
+
class MCPPermissionHandler {
|
|
6
|
+
constructor(mcpClient, permissionToolName, quiet = false) {
|
|
7
|
+
this.quiet = quiet;
|
|
8
|
+
this.permissionTool = null;
|
|
9
|
+
this.mcpClient = mcpClient;
|
|
10
|
+
if (permissionToolName) {
|
|
11
|
+
this.permissionTool = parser_1.MCPConfigParser.parsePermissionTool(permissionToolName);
|
|
12
|
+
if (!this.permissionTool) {
|
|
13
|
+
throw new Error(`Invalid permission tool format: ${permissionToolName}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async requestPermission(toolName, args, context) {
|
|
18
|
+
const result = await this.requestPermissionWithUpdate(toolName, args, context);
|
|
19
|
+
return result.allowed;
|
|
20
|
+
}
|
|
21
|
+
async requestPermissionWithUpdate(toolName, args, context) {
|
|
22
|
+
if (!this.permissionTool) {
|
|
23
|
+
return { allowed: true };
|
|
24
|
+
}
|
|
25
|
+
const connection = this.mcpClient.getConnection(this.permissionTool.serverName);
|
|
26
|
+
if (!connection) {
|
|
27
|
+
if (!this.quiet) {
|
|
28
|
+
console.warn(`Permission server "${this.permissionTool.serverName}" not connected`);
|
|
29
|
+
}
|
|
30
|
+
return { allowed: false };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
// Generate a unique tool use ID
|
|
34
|
+
const toolUseId = `tool-use-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
35
|
+
// Create the permission request in the required format
|
|
36
|
+
const permissionRequest = {
|
|
37
|
+
tool_name: toolName,
|
|
38
|
+
input: args,
|
|
39
|
+
tool_use_id: toolUseId,
|
|
40
|
+
// Add optional context fields
|
|
41
|
+
user_id: context.userId,
|
|
42
|
+
request_id: context.requestId,
|
|
43
|
+
metadata: context.metadata
|
|
44
|
+
};
|
|
45
|
+
const result = await connection.client.callTool({
|
|
46
|
+
name: this.permissionTool.toolName,
|
|
47
|
+
arguments: permissionRequest
|
|
48
|
+
});
|
|
49
|
+
if (result.isError) {
|
|
50
|
+
if (!this.quiet) {
|
|
51
|
+
console.error(`Permission check failed: ${result.text}`);
|
|
52
|
+
}
|
|
53
|
+
return { allowed: false };
|
|
54
|
+
}
|
|
55
|
+
const response = result.content?.[0];
|
|
56
|
+
if (response?.type === 'text') {
|
|
57
|
+
try {
|
|
58
|
+
// Parse the JSON response
|
|
59
|
+
const permissionResponse = JSON.parse(response.text);
|
|
60
|
+
return {
|
|
61
|
+
allowed: permissionResponse.behavior === 'allow',
|
|
62
|
+
updatedInput: permissionResponse.updatedInput
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (parseError) {
|
|
66
|
+
// Fallback to simple text parsing for backward compatibility
|
|
67
|
+
const text = response.text.toLowerCase();
|
|
68
|
+
const allowed = text === 'true' || text === 'yes' || text === 'allow' || text === 'granted';
|
|
69
|
+
return { allowed };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { allowed: false };
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (!this.quiet) {
|
|
76
|
+
console.error('Permission check error:', error);
|
|
77
|
+
}
|
|
78
|
+
return { allowed: false };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
setPermissionTool(toolName) {
|
|
82
|
+
this.permissionTool = parser_1.MCPConfigParser.parsePermissionTool(toolName);
|
|
83
|
+
if (!this.permissionTool) {
|
|
84
|
+
throw new Error(`Invalid permission tool format: ${toolName}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
getPermissionTool() {
|
|
88
|
+
return this.permissionTool?.originalName || null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.MCPPermissionHandler = MCPPermissionHandler;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class ToolFilter {
|
|
2
|
+
private allowedPatterns;
|
|
3
|
+
private disallowedPatterns;
|
|
4
|
+
private allowedExact;
|
|
5
|
+
private disallowedExact;
|
|
6
|
+
constructor(allowedTools?: string[], disallowedTools?: string[]);
|
|
7
|
+
private parsePatterns;
|
|
8
|
+
private matchesPatterns;
|
|
9
|
+
isAllowed(toolName: string): boolean;
|
|
10
|
+
filterTools<T extends {
|
|
11
|
+
name: string;
|
|
12
|
+
}>(tools: T[]): T[];
|
|
13
|
+
addAllowed(toolName: string): void;
|
|
14
|
+
addDisallowed(toolName: string): void;
|
|
15
|
+
removeAllowed(toolName: string): void;
|
|
16
|
+
removeDisallowed(toolName: string): void;
|
|
17
|
+
getAllowedList(): string[] | null;
|
|
18
|
+
getDisallowedList(): string[];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=tool-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-filter.d.ts","sourceRoot":"","sources":["../../src/tools/tool-filter.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAU;IACrB,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,kBAAkB,CAAW;IACrC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,eAAe,CAAc;gBAEzB,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE;IAW/D,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,eAAe;IAUvB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAepC,WAAW,CAAC,CAAC,SAAS;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE;IAIxD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWlC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWrC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKrC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKxC,cAAc,IAAI,MAAM,EAAE,GAAG,IAAI;IAKjC,iBAAiB,IAAI,MAAM,EAAE;CAG9B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ToolFilter = void 0;
|
|
4
|
+
class ToolFilter {
|
|
5
|
+
constructor(allowedTools, disallowedTools) {
|
|
6
|
+
// Separate wildcards from exact matches for performance
|
|
7
|
+
const allowed = this.parsePatterns(allowedTools || []);
|
|
8
|
+
const disallowed = this.parsePatterns(disallowedTools || []);
|
|
9
|
+
this.allowedPatterns = allowed.patterns;
|
|
10
|
+
this.allowedExact = allowed.exact;
|
|
11
|
+
this.disallowedPatterns = disallowed.patterns;
|
|
12
|
+
this.disallowedExact = disallowed.exact;
|
|
13
|
+
}
|
|
14
|
+
parsePatterns(tools) {
|
|
15
|
+
const patterns = [];
|
|
16
|
+
const exact = new Set();
|
|
17
|
+
for (const tool of tools) {
|
|
18
|
+
if (tool.includes('*')) {
|
|
19
|
+
// Convert wildcard pattern to regex
|
|
20
|
+
const regexPattern = tool
|
|
21
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars
|
|
22
|
+
.replace(/\*/g, '.*'); // Convert * to .*
|
|
23
|
+
patterns.push(new RegExp(`^${regexPattern}$`, 'i'));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Exact match
|
|
27
|
+
exact.add(tool);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { patterns, exact };
|
|
31
|
+
}
|
|
32
|
+
matchesPatterns(toolName, patterns, exact) {
|
|
33
|
+
// Check exact matches first (faster)
|
|
34
|
+
if (exact.has(toolName)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
// Check wildcard patterns
|
|
38
|
+
return patterns.some(pattern => pattern.test(toolName));
|
|
39
|
+
}
|
|
40
|
+
isAllowed(toolName) {
|
|
41
|
+
// Disallowed patterns take precedence
|
|
42
|
+
if (this.matchesPatterns(toolName, this.disallowedPatterns, this.disallowedExact)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// If no allowed patterns specified, allow all (except disallowed)
|
|
46
|
+
if (this.allowedPatterns.length === 0 && this.allowedExact.size === 0) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Check if matches any allowed pattern
|
|
50
|
+
return this.matchesPatterns(toolName, this.allowedPatterns, this.allowedExact);
|
|
51
|
+
}
|
|
52
|
+
filterTools(tools) {
|
|
53
|
+
return tools.filter(tool => this.isAllowed(tool.name));
|
|
54
|
+
}
|
|
55
|
+
addAllowed(toolName) {
|
|
56
|
+
if (toolName.includes('*')) {
|
|
57
|
+
const regexPattern = toolName
|
|
58
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
59
|
+
.replace(/\*/g, '.*');
|
|
60
|
+
this.allowedPatterns.push(new RegExp(`^${regexPattern}$`, 'i'));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.allowedExact.add(toolName);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
addDisallowed(toolName) {
|
|
67
|
+
if (toolName.includes('*')) {
|
|
68
|
+
const regexPattern = toolName
|
|
69
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
70
|
+
.replace(/\*/g, '.*');
|
|
71
|
+
this.disallowedPatterns.push(new RegExp(`^${regexPattern}$`, 'i'));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.disallowedExact.add(toolName);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
removeAllowed(toolName) {
|
|
78
|
+
this.allowedExact.delete(toolName);
|
|
79
|
+
// Note: Can't easily remove regex patterns, would need to track original strings
|
|
80
|
+
}
|
|
81
|
+
removeDisallowed(toolName) {
|
|
82
|
+
this.disallowedExact.delete(toolName);
|
|
83
|
+
// Note: Can't easily remove regex patterns, would need to track original strings
|
|
84
|
+
}
|
|
85
|
+
getAllowedList() {
|
|
86
|
+
const allowed = Array.from(this.allowedExact);
|
|
87
|
+
return allowed.length > 0 || this.allowedPatterns.length > 0 ? allowed : null;
|
|
88
|
+
}
|
|
89
|
+
getDisallowedList() {
|
|
90
|
+
return Array.from(this.disallowedExact);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.ToolFilter = ToolFilter;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface MCPServerConfig {
|
|
2
|
+
command: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
env?: Record<string, string | undefined>;
|
|
5
|
+
}
|
|
6
|
+
export interface DynamicCommandExec {
|
|
7
|
+
path: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
cwd?: string;
|
|
10
|
+
env?: Record<string, string>;
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
expect?: 'json' | 'text';
|
|
13
|
+
stream?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface DynamicCommand {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
parameters: {
|
|
19
|
+
type: 'object';
|
|
20
|
+
properties: Record<string, any>;
|
|
21
|
+
required?: string[];
|
|
22
|
+
};
|
|
23
|
+
exec: DynamicCommandExec;
|
|
24
|
+
}
|
|
25
|
+
export interface DynamicServerConfig {
|
|
26
|
+
commands: DynamicCommand[];
|
|
27
|
+
}
|
|
28
|
+
export type MCPServerConfigType = MCPServerConfig | DynamicServerConfig;
|
|
29
|
+
export interface MCPConfig {
|
|
30
|
+
mcpServers: Record<string, MCPServerConfigType>;
|
|
31
|
+
}
|
|
32
|
+
export interface MCPPluginOptions {
|
|
33
|
+
configPath?: string;
|
|
34
|
+
configString?: string;
|
|
35
|
+
servers?: Record<string, MCPServerConfigType>;
|
|
36
|
+
allowedTools?: string[];
|
|
37
|
+
disallowedTools?: string[];
|
|
38
|
+
permissionPromptTool?: string;
|
|
39
|
+
quiet?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ParsedPermissionTool {
|
|
42
|
+
serverName: string;
|
|
43
|
+
toolName: string;
|
|
44
|
+
originalName: string;
|
|
45
|
+
}
|
|
46
|
+
export interface PermissionRequest {
|
|
47
|
+
tool_name: string;
|
|
48
|
+
input: any;
|
|
49
|
+
tool_use_id: string;
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
}
|
|
52
|
+
export interface PermissionResponse {
|
|
53
|
+
behavior: 'allow' | 'deny';
|
|
54
|
+
updatedInput?: any;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE;QACV,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,mBAAmB,CAAC;AAExE,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,GAAG,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@majkapp/majk-chat-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP (Model Context Protocol) plugin for majk-chat",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"majk-mcp-server": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"test:watch": "jest --watch",
|
|
14
|
+
"test:coverage": "jest --coverage",
|
|
15
|
+
"lint": "eslint src --ext .ts",
|
|
16
|
+
"typecheck": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"majk-chat",
|
|
22
|
+
"ai",
|
|
23
|
+
"llm",
|
|
24
|
+
"tools"
|
|
25
|
+
],
|
|
26
|
+
"author": "Jules White",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
30
|
+
"@majkapp/majk-chat-core": "file:../majk-chat-core",
|
|
31
|
+
"commander": "^12.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jest": "^29.5.12",
|
|
35
|
+
"@types/node": "^20.11.19",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
|
37
|
+
"@typescript-eslint/parser": "^7.0.1",
|
|
38
|
+
"eslint": "^8.56.0",
|
|
39
|
+
"jest": "^29.7.0",
|
|
40
|
+
"ts-jest": "^29.1.2",
|
|
41
|
+
"ts-node": "^10.9.2",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@majkapp/majk-chat-core": "^1.0.0"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist/**/*",
|
|
52
|
+
"README.md"
|
|
53
|
+
]
|
|
54
|
+
}
|