@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.
@@ -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"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
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
+ }