mcp-garry-codes 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 +133 -0
- package/dist/config/env.d.ts +4 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +27 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/schema.d.ts +28 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +12 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/contracts/common.d.ts +53 -0
- package/dist/contracts/common.d.ts.map +1 -0
- package/dist/contracts/common.js +30 -0
- package/dist/contracts/common.js.map +1 -0
- package/dist/contracts/linter_contracts.d.ts +79 -0
- package/dist/contracts/linter_contracts.d.ts.map +1 -0
- package/dist/contracts/linter_contracts.js +16 -0
- package/dist/contracts/linter_contracts.js.map +1 -0
- package/dist/contracts/scaffold_contracts.d.ts +84 -0
- package/dist/contracts/scaffold_contracts.d.ts.map +1 -0
- package/dist/contracts/scaffold_contracts.js +27 -0
- package/dist/contracts/scaffold_contracts.js.map +1 -0
- package/dist/contracts/security_contracts.d.ts +37 -0
- package/dist/contracts/security_contracts.d.ts.map +1 -0
- package/dist/contracts/security_contracts.js +6 -0
- package/dist/contracts/security_contracts.js.map +1 -0
- package/dist/contracts/test_contracts.d.ts +42 -0
- package/dist/contracts/test_contracts.d.ts.map +1 -0
- package/dist/contracts/test_contracts.js +10 -0
- package/dist/contracts/test_contracts.js.map +1 -0
- package/dist/contracts/wiki_contracts.d.ts +96 -0
- package/dist/contracts/wiki_contracts.d.ts.map +1 -0
- package/dist/contracts/wiki_contracts.js +17 -0
- package/dist/contracts/wiki_contracts.js.map +1 -0
- package/dist/core/cache.d.ts +9 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +56 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/errors.d.ts +37 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +52 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/file_utils.d.ts +13 -0
- package/dist/core/file_utils.d.ts.map +1 -0
- package/dist/core/file_utils.js +90 -0
- package/dist/core/file_utils.js.map +1 -0
- package/dist/core/logger.d.ts +18 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +46 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/process_runner.d.ts +15 -0
- package/dist/core/process_runner.d.ts.map +1 -0
- package/dist/core/process_runner.js +64 -0
- package/dist/core/process_runner.js.map +1 -0
- package/dist/domain/glua/ast.d.ts +12 -0
- package/dist/domain/glua/ast.d.ts.map +1 -0
- package/dist/domain/glua/ast.js +176 -0
- package/dist/domain/glua/ast.js.map +1 -0
- package/dist/domain/glua/lexer.d.ts +42 -0
- package/dist/domain/glua/lexer.d.ts.map +1 -0
- package/dist/domain/glua/lexer.js +232 -0
- package/dist/domain/glua/lexer.js.map +1 -0
- package/dist/domain/networking/net_auditor.d.ts +5 -0
- package/dist/domain/networking/net_auditor.d.ts.map +1 -0
- package/dist/domain/networking/net_auditor.js +188 -0
- package/dist/domain/networking/net_auditor.js.map +1 -0
- package/dist/domain/packaging/validator.d.ts +5 -0
- package/dist/domain/packaging/validator.d.ts.map +1 -0
- package/dist/domain/packaging/validator.js +121 -0
- package/dist/domain/packaging/validator.js.map +1 -0
- package/dist/domain/performance/hot_path_analyzer.d.ts +5 -0
- package/dist/domain/performance/hot_path_analyzer.d.ts.map +1 -0
- package/dist/domain/performance/hot_path_analyzer.js +127 -0
- package/dist/domain/performance/hot_path_analyzer.js.map +1 -0
- package/dist/domain/scaffolding/templates.d.ts +5 -0
- package/dist/domain/scaffolding/templates.d.ts.map +1 -0
- package/dist/domain/scaffolding/templates.js +219 -0
- package/dist/domain/scaffolding/templates.js.map +1 -0
- package/dist/domain/wiki/offline_db.d.ts +3 -0
- package/dist/domain/wiki/offline_db.d.ts.map +1 -0
- package/dist/domain/wiki/offline_db.js +534 -0
- package/dist/domain/wiki/offline_db.js.map +1 -0
- package/dist/domain/wiki/types.d.ts +29 -0
- package/dist/domain/wiki/types.d.ts.map +1 -0
- package/dist/domain/wiki/types.js +2 -0
- package/dist/domain/wiki/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/glualint_adapter.d.ts +6 -0
- package/dist/integrations/glualint_adapter.d.ts.map +1 -0
- package/dist/integrations/glualint_adapter.js +62 -0
- package/dist/integrations/glualint_adapter.js.map +1 -0
- package/dist/integrations/python_test_harness.d.ts +5 -0
- package/dist/integrations/python_test_harness.d.ts.map +1 -0
- package/dist/integrations/python_test_harness.js +67 -0
- package/dist/integrations/python_test_harness.js.map +1 -0
- package/dist/integrations/web_search_adapter.d.ts +6 -0
- package/dist/integrations/web_search_adapter.d.ts.map +1 -0
- package/dist/integrations/web_search_adapter.js +80 -0
- package/dist/integrations/web_search_adapter.js.map +1 -0
- package/dist/integrations/wiki_client.d.ts +13 -0
- package/dist/integrations/wiki_client.d.ts.map +1 -0
- package/dist/integrations/wiki_client.js +110 -0
- package/dist/integrations/wiki_client.js.map +1 -0
- package/dist/prompts/index.d.ts +73 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +127 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/realms_resources.d.ts +2 -0
- package/dist/resources/realms_resources.d.ts.map +1 -0
- package/dist/resources/realms_resources.js +30 -0
- package/dist/resources/realms_resources.js.map +1 -0
- package/dist/resources/schema_resources.d.ts +2 -0
- package/dist/resources/schema_resources.d.ts.map +1 -0
- package/dist/resources/schema_resources.js +43 -0
- package/dist/resources/schema_resources.js.map +1 -0
- package/dist/resources/wiki_resources.d.ts +3 -0
- package/dist/resources/wiki_resources.d.ts.map +1 -0
- package/dist/resources/wiki_resources.js +31 -0
- package/dist/resources/wiki_resources.js.map +1 -0
- package/dist/security/path_guard.d.ts +8 -0
- package/dist/security/path_guard.d.ts.map +1 -0
- package/dist/security/path_guard.js +37 -0
- package/dist/security/path_guard.js.map +1 -0
- package/dist/security/process_guard.d.ts +8 -0
- package/dist/security/process_guard.d.ts.map +1 -0
- package/dist/security/process_guard.js +26 -0
- package/dist/security/process_guard.js.map +1 -0
- package/dist/security/redaction.d.ts +6 -0
- package/dist/security/redaction.d.ts.map +1 -0
- package/dist/security/redaction.js +49 -0
- package/dist/security/redaction.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +364 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/analyze_performance.d.ts +4 -0
- package/dist/tools/analyze_performance.d.ts.map +1 -0
- package/dist/tools/analyze_performance.js +20 -0
- package/dist/tools/analyze_performance.js.map +1 -0
- package/dist/tools/audit_net_security.d.ts +4 -0
- package/dist/tools/audit_net_security.d.ts.map +1 -0
- package/dist/tools/audit_net_security.js +19 -0
- package/dist/tools/audit_net_security.js.map +1 -0
- package/dist/tools/get_gmod_symbol.d.ts +4 -0
- package/dist/tools/get_gmod_symbol.d.ts.map +1 -0
- package/dist/tools/get_gmod_symbol.js +30 -0
- package/dist/tools/get_gmod_symbol.js.map +1 -0
- package/dist/tools/lint_glua.d.ts +4 -0
- package/dist/tools/lint_glua.d.ts.map +1 -0
- package/dist/tools/lint_glua.js +24 -0
- package/dist/tools/lint_glua.js.map +1 -0
- package/dist/tools/package_and_validate.d.ts +4 -0
- package/dist/tools/package_and_validate.d.ts.map +1 -0
- package/dist/tools/package_and_validate.js +23 -0
- package/dist/tools/package_and_validate.js.map +1 -0
- package/dist/tools/run_glua_test.d.ts +4 -0
- package/dist/tools/run_glua_test.d.ts.map +1 -0
- package/dist/tools/run_glua_test.js +15 -0
- package/dist/tools/run_glua_test.js.map +1 -0
- package/dist/tools/scaffold_gmod_component.d.ts +4 -0
- package/dist/tools/scaffold_gmod_component.d.ts.map +1 -0
- package/dist/tools/scaffold_gmod_component.js +16 -0
- package/dist/tools/scaffold_gmod_component.js.map +1 -0
- package/dist/tools/search_gmod_wiki.d.ts +4 -0
- package/dist/tools/search_gmod_wiki.d.ts.map +1 -0
- package/dist/tools/search_gmod_wiki.js +30 -0
- package/dist/tools/search_gmod_wiki.js.map +1 -0
- package/dist/tools/web_search_glua.d.ts +4 -0
- package/dist/tools/web_search_glua.d.ts.map +1 -0
- package/dist/tools/web_search_glua.js +15 -0
- package/dist/tools/web_search_glua.js.map +1 -0
- package/package.json +58 -0
- package/src/test_harness/lua/glua_mock.lua +383 -0
- package/src/test_harness/python/gmod_tester.py +156 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema_resources.d.ts","sourceRoot":"","sources":["../../src/resources/schema_resources.ts"],"names":[],"mappings":"AAAA,wBAAgB,yBAAyB,IAAI,MAAM,CA6ClD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export function getAddonJsonSchemaContent() {
|
|
2
|
+
return JSON.stringify({
|
|
3
|
+
description: "Garry's Mod addon.json Steam Workshop Schema",
|
|
4
|
+
type: "object",
|
|
5
|
+
required: ["title", "type", "tags", "ignore"],
|
|
6
|
+
properties: {
|
|
7
|
+
title: {
|
|
8
|
+
type: "string",
|
|
9
|
+
description: "Human-readable name of the addon displayed in Steam Workshop."
|
|
10
|
+
},
|
|
11
|
+
type: {
|
|
12
|
+
type: "string",
|
|
13
|
+
enum: [
|
|
14
|
+
"ServerContent",
|
|
15
|
+
"gamemode",
|
|
16
|
+
"map",
|
|
17
|
+
"weapon",
|
|
18
|
+
"vehicle",
|
|
19
|
+
"npc",
|
|
20
|
+
"tool",
|
|
21
|
+
"effects",
|
|
22
|
+
"model"
|
|
23
|
+
],
|
|
24
|
+
description: "Addon primary category classification."
|
|
25
|
+
},
|
|
26
|
+
tags: {
|
|
27
|
+
type: "array",
|
|
28
|
+
maxItems: 2,
|
|
29
|
+
items: {
|
|
30
|
+
type: "string",
|
|
31
|
+
enum: ["fun", "roleplay", "scenic", "movie", "realism", "comic", "build"]
|
|
32
|
+
},
|
|
33
|
+
description: "Up to 2 tags to categorize the addon on Steam."
|
|
34
|
+
},
|
|
35
|
+
ignore: {
|
|
36
|
+
type: "array",
|
|
37
|
+
items: { type: "string" },
|
|
38
|
+
description: "Glob patterns of files to exclude from compiled GMA package."
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}, null, 2);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=schema_resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema_resources.js","sourceRoot":"","sources":["../../src/resources/schema_resources.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,yBAAyB;IACvC,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,WAAW,EAAE,8CAA8C;QAC3D,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;QAC7C,UAAU,EAAE;YACV,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+DAA+D;aAC7E;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE;oBACJ,eAAe;oBACf,UAAU;oBACV,KAAK;oBACL,QAAQ;oBACR,SAAS;oBACT,KAAK;oBACL,MAAM;oBACN,SAAS;oBACT,OAAO;iBACR;gBACD,WAAW,EAAE,wCAAwC;aACtD;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,OAAO;gBACb,QAAQ,EAAE,CAAC;gBACX,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;iBAC1E;gBACD,WAAW,EAAE,gDAAgD;aAC9D;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EAAE,8DAA8D;aAC5E;SACF;KACF,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki_resources.d.ts","sourceRoot":"","sources":["../../src/resources/wiki_resources.ts"],"names":[],"mappings":"AAEA,wBAAgB,uBAAuB,IAAI,MAAM,CAkBhD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAiBpD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GMOD_OFFLINE_DOCS } from "../domain/wiki/offline_db.js";
|
|
2
|
+
export function getHooksResourceContent() {
|
|
3
|
+
const hooks = GMOD_OFFLINE_DOCS.filter(d => d.category === "hooks");
|
|
4
|
+
return JSON.stringify({
|
|
5
|
+
description: "Garry's Mod Core Hooks Reference",
|
|
6
|
+
total: hooks.length,
|
|
7
|
+
hooks: hooks.map(h => ({
|
|
8
|
+
name: h.symbol,
|
|
9
|
+
realm: h.realm,
|
|
10
|
+
signature: h.signature,
|
|
11
|
+
description: h.description,
|
|
12
|
+
warnings: h.warnings,
|
|
13
|
+
wiki_url: h.wiki_url
|
|
14
|
+
}))
|
|
15
|
+
}, null, 2);
|
|
16
|
+
}
|
|
17
|
+
export function getLibrariesResourceContent() {
|
|
18
|
+
const libs = GMOD_OFFLINE_DOCS.filter(d => d.category === "functions" || d.category === "libraries");
|
|
19
|
+
return JSON.stringify({
|
|
20
|
+
description: "Garry's Mod Core Functions & Libraries Reference",
|
|
21
|
+
total: libs.length,
|
|
22
|
+
entries: libs.map(l => ({
|
|
23
|
+
name: l.symbol,
|
|
24
|
+
realm: l.realm,
|
|
25
|
+
signature: l.signature,
|
|
26
|
+
description: l.description,
|
|
27
|
+
wiki_url: l.wiki_url
|
|
28
|
+
}))
|
|
29
|
+
}, null, 2);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=wiki_resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki_resources.js","sourceRoot":"","sources":["../../src/resources/wiki_resources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,UAAU,uBAAuB;IACrC,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IACpE,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,WAAW,EAAE,kCAAkC;QAC/C,KAAK,EAAE,KAAK,CAAC,MAAM;QACnB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACrB,IAAI,EAAE,CAAC,CAAC,MAAM;YACd,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;SACrB,CAAC,CAAC;KACJ,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B;IACzC,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,WAAW,IAAI,CAAC,CAAC,QAAQ,KAAK,WAAW,CAAC,CAAC;IACrG,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,WAAW,EAAE,kDAAkD;QAC/D,KAAK,EAAE,IAAI,CAAC,MAAM;QAClB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtB,IAAI,EAAE,CAAC,CAAC,MAAM;YACd,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;SACrB,CAAC,CAAC;KACJ,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Guard: Enforces strict jail boundaries on workspace files to prevent directory traversal attacks.
|
|
3
|
+
*/
|
|
4
|
+
export declare class PathGuard {
|
|
5
|
+
static resolveSafePath(filePath: string, customRoot?: string): string;
|
|
6
|
+
static isSafePath(filePath: string, customRoot?: string): boolean;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=path_guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path_guard.d.ts","sourceRoot":"","sources":["../../src/security/path_guard.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,qBAAa,SAAS;WACN,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM;WAyB9D,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO;CAQzE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { GModErrorCode, GModMCPError } from "../core/errors.js";
|
|
3
|
+
import { getConfig } from "../config/env.js";
|
|
4
|
+
/**
|
|
5
|
+
* Path Guard: Enforces strict jail boundaries on workspace files to prevent directory traversal attacks.
|
|
6
|
+
*/
|
|
7
|
+
export class PathGuard {
|
|
8
|
+
static resolveSafePath(filePath, customRoot) {
|
|
9
|
+
const root = path.resolve(customRoot ?? getConfig().WORKSPACE_ROOT);
|
|
10
|
+
const resolved = path.resolve(root, filePath);
|
|
11
|
+
// Normalize both for platform-agnostic comparison (especially Windows lowercasing/slashes)
|
|
12
|
+
const normalizedRoot = path.normalize(root).toLowerCase();
|
|
13
|
+
const normalizedResolved = path.normalize(resolved).toLowerCase();
|
|
14
|
+
// Check if the resolved path is either the root itself or starts with root + separator
|
|
15
|
+
const isInside = normalizedResolved === normalizedRoot ||
|
|
16
|
+
normalizedResolved.startsWith(normalizedRoot + path.sep);
|
|
17
|
+
if (!isInside) {
|
|
18
|
+
throw new GModMCPError({
|
|
19
|
+
code: GModErrorCode.PATH_JAIL_VIOLATION,
|
|
20
|
+
message: `Security violation: Path '${filePath}' attempts to escape the allowed workspace boundary '${root}'.`,
|
|
21
|
+
remediation: "Ensure the path stays within the workspace root directory.",
|
|
22
|
+
context: { attemptedPath: filePath, workspaceRoot: root }
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
static isSafePath(filePath, customRoot) {
|
|
28
|
+
try {
|
|
29
|
+
this.resolveSafePath(filePath, customRoot);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=path_guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path_guard.js","sourceRoot":"","sources":["../../src/security/path_guard.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;GAEG;AACH,MAAM,OAAO,SAAS;IACb,MAAM,CAAC,eAAe,CAAC,QAAgB,EAAE,UAAmB;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,SAAS,EAAE,CAAC,cAAc,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE9C,2FAA2F;QAC3F,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1D,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;QAElE,uFAAuF;QACvF,MAAM,QAAQ,GACZ,kBAAkB,KAAK,cAAc;YACrC,kBAAkB,CAAC,UAAU,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QAE3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,YAAY,CAAC;gBACrB,IAAI,EAAE,aAAa,CAAC,mBAAmB;gBACvC,OAAO,EAAE,6BAA6B,QAAQ,wDAAwD,IAAI,IAAI;gBAC9G,WAAW,EAAE,4DAA4D;gBACzE,OAAO,EAAE,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE;aAC1D,CAAC,CAAC;QACL,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEM,MAAM,CAAC,UAAU,CAAC,QAAgB,EAAE,UAAmB;QAC5D,IAAI,CAAC;YACH,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process Guard: Restricts command execution to safe argument arrays, blocking shell metacharacters.
|
|
3
|
+
*/
|
|
4
|
+
export declare class ProcessGuard {
|
|
5
|
+
private static DANGEROUS_PATTERNS;
|
|
6
|
+
static validateArgs(args: string[]): void;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=process_guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process_guard.d.ts","sourceRoot":"","sources":["../../src/security/process_guard.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,YAAY;IAEvB,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAG/B;WAEY,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;CAcjD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { GModErrorCode, GModMCPError } from "../core/errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Process Guard: Restricts command execution to safe argument arrays, blocking shell metacharacters.
|
|
4
|
+
*/
|
|
5
|
+
export class ProcessGuard {
|
|
6
|
+
// Disallowed argument strings that could trick argument parsers
|
|
7
|
+
static DANGEROUS_PATTERNS = [
|
|
8
|
+
/[;&|`$]/, // Shell command delimiters
|
|
9
|
+
/\x00/ // Null bytes
|
|
10
|
+
];
|
|
11
|
+
static validateArgs(args) {
|
|
12
|
+
for (const arg of args) {
|
|
13
|
+
for (const pattern of this.DANGEROUS_PATTERNS) {
|
|
14
|
+
if (pattern.test(arg)) {
|
|
15
|
+
throw new GModMCPError({
|
|
16
|
+
code: GModErrorCode.COMMAND_INJECTION_DETECTED,
|
|
17
|
+
message: `Command injection risk detected in argument: '${arg}'`,
|
|
18
|
+
remediation: "Ensure arguments do not contain shell control characters or null bytes.",
|
|
19
|
+
context: { arg }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=process_guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process_guard.js","sourceRoot":"","sources":["../../src/security/process_guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEhE;;GAEG;AACH,MAAM,OAAO,YAAY;IACvB,gEAAgE;IACxD,MAAM,CAAC,kBAAkB,GAAG;QAClC,SAAS,EAAE,2BAA2B;QACtC,MAAM,CAAC,aAAa;KACrB,CAAC;IAEK,MAAM,CAAC,YAAY,CAAC,IAAc;QACvC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC9C,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,IAAI,YAAY,CAAC;wBACrB,IAAI,EAAE,aAAa,CAAC,0BAA0B;wBAC9C,OAAO,EAAE,iDAAiD,GAAG,GAAG;wBAChE,WAAW,EAAE,yEAAyE;wBACtF,OAAO,EAAE,EAAE,GAAG,EAAE;qBACjB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction utility for protecting sensitive credentials (Steam API keys, RCON passwords, MySQL strings).
|
|
3
|
+
*/
|
|
4
|
+
export declare function redactSensitiveData(input: string): string;
|
|
5
|
+
export declare function sanitizeEnvelope<T>(data: T): T;
|
|
6
|
+
//# sourceMappingURL=redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../../src/security/redaction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyBH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAe9C"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction utility for protecting sensitive credentials (Steam API keys, RCON passwords, MySQL strings).
|
|
3
|
+
*/
|
|
4
|
+
const REDACTION_PATTERNS = [
|
|
5
|
+
// Steam Web API Key: 32 hex chars
|
|
6
|
+
{
|
|
7
|
+
pattern: /\b[0-9A-F]{32}\b/gi,
|
|
8
|
+
replacement: "[REDACTED_STEAM_KEY]"
|
|
9
|
+
},
|
|
10
|
+
// RCON password: rcon_password "secret" or rcon_password = "secret"
|
|
11
|
+
{
|
|
12
|
+
pattern: /(rcon_password\s*[="']\s*)([^"'\s;]+)/gi,
|
|
13
|
+
replacement: "$1[REDACTED_RCON_PASSWORD]"
|
|
14
|
+
},
|
|
15
|
+
// MySQL connection string passwords
|
|
16
|
+
{
|
|
17
|
+
pattern: /(password\s*[:=]\s*["'])([^"']+)(["'])/gi,
|
|
18
|
+
replacement: "$1[REDACTED_PASSWORD]$3"
|
|
19
|
+
},
|
|
20
|
+
// Generic Bearer / Secret tokens
|
|
21
|
+
{
|
|
22
|
+
pattern: /(bearer\s+)([a-zA-Z0-9_\-\.]{15,})/gi,
|
|
23
|
+
replacement: "$1[REDACTED_TOKEN]"
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
export function redactSensitiveData(input) {
|
|
27
|
+
let result = input;
|
|
28
|
+
for (const { pattern, replacement } of REDACTION_PATTERNS) {
|
|
29
|
+
result = result.replace(pattern, replacement);
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
export function sanitizeEnvelope(data) {
|
|
34
|
+
if (typeof data === "string") {
|
|
35
|
+
return redactSensitiveData(data);
|
|
36
|
+
}
|
|
37
|
+
if (Array.isArray(data)) {
|
|
38
|
+
return data.map(item => sanitizeEnvelope(item));
|
|
39
|
+
}
|
|
40
|
+
if (data !== null && typeof data === "object") {
|
|
41
|
+
const sanitizedObj = {};
|
|
42
|
+
for (const [key, value] of Object.entries(data)) {
|
|
43
|
+
sanitizedObj[key] = sanitizeEnvelope(value);
|
|
44
|
+
}
|
|
45
|
+
return sanitizedObj;
|
|
46
|
+
}
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redaction.js","sourceRoot":"","sources":["../../src/security/redaction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,kBAAkB,GAAoD;IAC1E,kCAAkC;IAClC;QACE,OAAO,EAAE,oBAAoB;QAC7B,WAAW,EAAE,sBAAsB;KACpC;IACD,oEAAoE;IACpE;QACE,OAAO,EAAE,yCAAyC;QAClD,WAAW,EAAE,4BAA4B;KAC1C;IACD,oCAAoC;IACpC;QACE,OAAO,EAAE,0CAA0C;QACnD,WAAW,EAAE,yBAAyB;KACvC;IACD,iCAAiC;IACjC;QACE,OAAO,EAAE,sCAAsC;QAC/C,WAAW,EAAE,oBAAoB;KAClC;CACF,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,kBAAkB,EAAE,CAAC;QAC1D,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAI,IAAO;IACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,mBAAmB,CAAC,IAAI,CAAiB,CAAC;IACnD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAiB,CAAC;IAClE,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,YAAY,GAA4B,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YAC3E,YAAY,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,YAAiB,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAoCnE,wBAAgB,YAAY,IAAI,MAAM,CAkWrC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { logger } from "./core/logger.js";
|
|
4
|
+
import { sanitizeEnvelope } from "./security/redaction.js";
|
|
5
|
+
// Tool handlers
|
|
6
|
+
import { handleSearchGModWiki } from "./tools/search_gmod_wiki.js";
|
|
7
|
+
import { handleGetGModSymbol } from "./tools/get_gmod_symbol.js";
|
|
8
|
+
import { handleWebSearchGLua } from "./tools/web_search_glua.js";
|
|
9
|
+
import { handleLintGLua } from "./tools/lint_glua.js";
|
|
10
|
+
import { handleAuditNetSecurity } from "./tools/audit_net_security.js";
|
|
11
|
+
import { handleAnalyzePerformance } from "./tools/analyze_performance.js";
|
|
12
|
+
import { handleScaffoldGModComponent } from "./tools/scaffold_gmod_component.js";
|
|
13
|
+
import { handleRunGLuaTest } from "./tools/run_glua_test.js";
|
|
14
|
+
import { handlePackageAndValidate } from "./tools/package_and_validate.js";
|
|
15
|
+
// Resources
|
|
16
|
+
import { getHooksResourceContent, getLibrariesResourceContent } from "./resources/wiki_resources.js";
|
|
17
|
+
import { getRealmsMatrixContent } from "./resources/realms_resources.js";
|
|
18
|
+
import { getAddonJsonSchemaContent } from "./resources/schema_resources.js";
|
|
19
|
+
// Prompts
|
|
20
|
+
import { AUDIT_ADDON_PROMPT, FIX_NET_EXPLOIT_PROMPT, SCAFFOLD_SWEP_PROMPT, BUILD_DERMA_MENU_PROMPT } from "./prompts/index.js";
|
|
21
|
+
export function createServer() {
|
|
22
|
+
const server = new Server({
|
|
23
|
+
name: "mcp-garry-codes",
|
|
24
|
+
version: "1.0.0"
|
|
25
|
+
}, {
|
|
26
|
+
capabilities: {
|
|
27
|
+
tools: {},
|
|
28
|
+
resources: {},
|
|
29
|
+
prompts: {}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
// 1. Tools Registration
|
|
33
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
34
|
+
return {
|
|
35
|
+
tools: [
|
|
36
|
+
{
|
|
37
|
+
name: "search_gmod_wiki",
|
|
38
|
+
description: "Query Facepunch Garry's Mod Wiki and indexed community documentation for functions, hooks, libraries, panels, and engine behavior.",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
query: { type: "string", description: "Search term (e.g. 'net.Receive', 'GM:PlayerInitialSpawn')" },
|
|
43
|
+
category: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: ["functions", "hooks", "libraries", "panels", "enums", "all"],
|
|
46
|
+
default: "all",
|
|
47
|
+
description: "Filter search by category"
|
|
48
|
+
},
|
|
49
|
+
realm: {
|
|
50
|
+
type: "string",
|
|
51
|
+
enum: ["server", "client", "shared", "menu", "all"],
|
|
52
|
+
default: "all",
|
|
53
|
+
description: "Filter by execution realm"
|
|
54
|
+
},
|
|
55
|
+
max_results: { type: "integer", minimum: 1, maximum: 20, default: 5, description: "Max results to return" }
|
|
56
|
+
},
|
|
57
|
+
required: ["query"]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "get_gmod_symbol",
|
|
62
|
+
description: "Retrieve comprehensive documentation for a specific Garry's Mod symbol including realm, arguments, return types, code examples, and caveats.",
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: "object",
|
|
65
|
+
properties: {
|
|
66
|
+
symbol: { type: "string", description: "Exact symbol name (e.g. 'surface.DrawPoly', 'util.AddNetworkString')" },
|
|
67
|
+
include_examples: { type: "boolean", default: true, description: "Include official usage examples" }
|
|
68
|
+
},
|
|
69
|
+
required: ["symbol"]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "web_search_glua",
|
|
74
|
+
description: "Search GMod developer communities, GitHub repositories, and forums for undocumented quirks and solutions.",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: {
|
|
78
|
+
query: { type: "string", description: "Target query" },
|
|
79
|
+
domain_filter: {
|
|
80
|
+
type: "string",
|
|
81
|
+
enum: ["github", "facepunch", "reddit", "steam", "all"],
|
|
82
|
+
default: "all",
|
|
83
|
+
description: "Target community platform"
|
|
84
|
+
},
|
|
85
|
+
max_results: { type: "integer", minimum: 1, maximum: 10, default: 5, description: "Max results" }
|
|
86
|
+
},
|
|
87
|
+
required: ["query"]
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "lint_glua",
|
|
92
|
+
description: "Deep static analysis on GLua code catching syntax errors, realm mismatches, global pollution, and missing AddCSLuaFile.",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
file_path: { type: "string", description: "Workspace-relative or absolute path to .lua file" },
|
|
97
|
+
code_content: { type: "string", description: "Direct Lua code string to analyze (if file_path omitted)" },
|
|
98
|
+
target_realm: {
|
|
99
|
+
type: "string",
|
|
100
|
+
enum: ["server", "client", "shared", "menu", "auto"],
|
|
101
|
+
default: "auto",
|
|
102
|
+
description: "Target realm context"
|
|
103
|
+
},
|
|
104
|
+
fix_formatting: { type: "boolean", default: false, description: "Auto-fix code if possible" }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "audit_net_security",
|
|
110
|
+
description: "Analyze network message graphs across an addon to identify unauthenticated receivers, SQL injection vectors, and missing network strings.",
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
addon_dir: { type: "string", description: "Root directory of addon (defaults to workspace)" },
|
|
115
|
+
file_paths: { type: "array", items: { type: "string" }, description: "Specific files to scan" }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "analyze_performance",
|
|
121
|
+
description: "Profile GLua code for runtime performance traps: Zero-GC violations in render/think hooks, uncached fonts/materials, unlocalized globals.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
file_path: { type: "string", description: "Workspace path to .lua file" },
|
|
126
|
+
code_content: { type: "string", description: "Direct Lua code snippet" }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "scaffold_gmod_component",
|
|
132
|
+
description: "Generate idiomatically realm-separated Garry's Mod boilerplate for SWEP, SENT, VGUI panels, HUDs, or gamemodes.",
|
|
133
|
+
inputSchema: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
component_type: {
|
|
137
|
+
type: "string",
|
|
138
|
+
enum: ["swep", "sent", "vgui_panel", "custom_tool", "hud", "gamemode", "addon_skeleton"],
|
|
139
|
+
description: "Type of Garry's Mod component to scaffold"
|
|
140
|
+
},
|
|
141
|
+
name: { type: "string", description: "Identifier (e.g. 'weapon_plasma_rifle', 'ent_crate')" },
|
|
142
|
+
output_dir: { type: "string", description: "Workspace folder to write generated files to" },
|
|
143
|
+
options: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
category: { type: "string" },
|
|
147
|
+
base: { type: "string" },
|
|
148
|
+
author: { type: "string" },
|
|
149
|
+
printable_name: { type: "string" },
|
|
150
|
+
vgui_type: { type: "string", enum: ["frame", "button", "scroll", "custom"] }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
required: ["component_type", "name"]
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "run_glua_test",
|
|
159
|
+
description: "Execute headless GLua unit tests using the embedded Source Engine mock runtime.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
test_file_path: { type: "string", description: "Workspace path to test script" },
|
|
164
|
+
code_to_test: { type: "string", description: "Inline GLua test code with assertions" },
|
|
165
|
+
mock_realm: { type: "string", enum: ["client", "server", "shared"], default: "shared" },
|
|
166
|
+
timeout_ms: { type: "integer", default: 5000, description: "Max timeout in ms" }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "package_and_validate",
|
|
172
|
+
description: "Verify an addon directory against Steam Workshop and GMA packaging rules (addon.json validation, file whitelist, casing).",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
addon_dir: { type: "string", description: "Root directory of addon (defaults to workspace)" },
|
|
177
|
+
build_gma: { type: "boolean", default: false, description: "Compile .gma package if gmad is available" },
|
|
178
|
+
output_gma_path: { type: "string", description: "Destination path for .gma" }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
// 2. Tools Execution
|
|
186
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
187
|
+
const { name, arguments: args } = request.params;
|
|
188
|
+
logger.info(`Tool called: ${name}`, { args });
|
|
189
|
+
try {
|
|
190
|
+
let result;
|
|
191
|
+
switch (name) {
|
|
192
|
+
case "search_gmod_wiki":
|
|
193
|
+
result = await handleSearchGModWiki(args);
|
|
194
|
+
break;
|
|
195
|
+
case "get_gmod_symbol":
|
|
196
|
+
result = await handleGetGModSymbol(args);
|
|
197
|
+
break;
|
|
198
|
+
case "web_search_glua":
|
|
199
|
+
result = await handleWebSearchGLua(args);
|
|
200
|
+
break;
|
|
201
|
+
case "lint_glua":
|
|
202
|
+
result = await handleLintGLua(args);
|
|
203
|
+
break;
|
|
204
|
+
case "audit_net_security":
|
|
205
|
+
result = await handleAuditNetSecurity(args);
|
|
206
|
+
break;
|
|
207
|
+
case "analyze_performance":
|
|
208
|
+
result = await handleAnalyzePerformance(args);
|
|
209
|
+
break;
|
|
210
|
+
case "scaffold_gmod_component":
|
|
211
|
+
result = await handleScaffoldGModComponent(args);
|
|
212
|
+
break;
|
|
213
|
+
case "run_glua_test":
|
|
214
|
+
result = await handleRunGLuaTest(args);
|
|
215
|
+
break;
|
|
216
|
+
case "package_and_validate":
|
|
217
|
+
result = await handlePackageAndValidate(args);
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: JSON.stringify({
|
|
225
|
+
status: "failed",
|
|
226
|
+
summary: `Unknown tool '${name}'.`,
|
|
227
|
+
data: null,
|
|
228
|
+
warnings: [`Tool '${name}' is not registered on this server.`]
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
isError: true
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// Redact sensitive keys before transmission
|
|
236
|
+
const sanitized = sanitizeEnvelope(result);
|
|
237
|
+
return {
|
|
238
|
+
content: [
|
|
239
|
+
{
|
|
240
|
+
type: "text",
|
|
241
|
+
text: JSON.stringify(sanitized, null, 2)
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
isError: sanitized.status === "failed"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
249
|
+
logger.error(`Error executing tool '${name}': ${errorMsg}`);
|
|
250
|
+
return {
|
|
251
|
+
content: [
|
|
252
|
+
{
|
|
253
|
+
type: "text",
|
|
254
|
+
text: JSON.stringify({
|
|
255
|
+
status: "failed",
|
|
256
|
+
summary: `Tool execution failed: ${errorMsg}`,
|
|
257
|
+
data: null,
|
|
258
|
+
warnings: [errorMsg]
|
|
259
|
+
}, null, 2)
|
|
260
|
+
}
|
|
261
|
+
],
|
|
262
|
+
isError: true
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
// 3. Resources Registration
|
|
267
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
268
|
+
return {
|
|
269
|
+
resources: [
|
|
270
|
+
{
|
|
271
|
+
uri: "gmod://wiki/hooks",
|
|
272
|
+
name: "Garry's Mod Core Hooks Reference",
|
|
273
|
+
mimeType: "application/json",
|
|
274
|
+
description: "Authoritative list of Garry's Mod hooks across all realms."
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
uri: "gmod://wiki/libraries",
|
|
278
|
+
name: "Garry's Mod Core Libraries & Global Functions",
|
|
279
|
+
mimeType: "application/json",
|
|
280
|
+
description: "Core functions and libraries available in GLua."
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
uri: "gmod://realms/matrix",
|
|
284
|
+
name: "Garry's Mod Realm Matrix",
|
|
285
|
+
mimeType: "application/json",
|
|
286
|
+
description: "Execution realm boundaries, powers, and restrictions."
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
uri: "gmod://schemas/addon_json",
|
|
290
|
+
name: "Steam Workshop addon.json Schema",
|
|
291
|
+
mimeType: "application/json",
|
|
292
|
+
description: "Schema specification for Garry's Mod addon.json."
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
};
|
|
296
|
+
});
|
|
297
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
298
|
+
const { uri } = request.params;
|
|
299
|
+
switch (uri) {
|
|
300
|
+
case "gmod://wiki/hooks":
|
|
301
|
+
return {
|
|
302
|
+
contents: [{ uri, mimeType: "application/json", text: getHooksResourceContent() }]
|
|
303
|
+
};
|
|
304
|
+
case "gmod://wiki/libraries":
|
|
305
|
+
return {
|
|
306
|
+
contents: [{ uri, mimeType: "application/json", text: getLibrariesResourceContent() }]
|
|
307
|
+
};
|
|
308
|
+
case "gmod://realms/matrix":
|
|
309
|
+
return {
|
|
310
|
+
contents: [{ uri, mimeType: "application/json", text: getRealmsMatrixContent() }]
|
|
311
|
+
};
|
|
312
|
+
case "gmod://schemas/addon_json":
|
|
313
|
+
return {
|
|
314
|
+
contents: [{ uri, mimeType: "application/json", text: getAddonJsonSchemaContent() }]
|
|
315
|
+
};
|
|
316
|
+
default:
|
|
317
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// 4. Prompts Registration
|
|
321
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
322
|
+
return {
|
|
323
|
+
prompts: [
|
|
324
|
+
{
|
|
325
|
+
name: AUDIT_ADDON_PROMPT.name,
|
|
326
|
+
description: AUDIT_ADDON_PROMPT.description,
|
|
327
|
+
arguments: AUDIT_ADDON_PROMPT.arguments
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: FIX_NET_EXPLOIT_PROMPT.name,
|
|
331
|
+
description: FIX_NET_EXPLOIT_PROMPT.description,
|
|
332
|
+
arguments: FIX_NET_EXPLOIT_PROMPT.arguments
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: SCAFFOLD_SWEP_PROMPT.name,
|
|
336
|
+
description: SCAFFOLD_SWEP_PROMPT.description,
|
|
337
|
+
arguments: SCAFFOLD_SWEP_PROMPT.arguments
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: BUILD_DERMA_MENU_PROMPT.name,
|
|
341
|
+
description: BUILD_DERMA_MENU_PROMPT.description,
|
|
342
|
+
arguments: BUILD_DERMA_MENU_PROMPT.arguments
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
};
|
|
346
|
+
});
|
|
347
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
348
|
+
const { name, arguments: args } = request.params;
|
|
349
|
+
switch (name) {
|
|
350
|
+
case AUDIT_ADDON_PROMPT.name:
|
|
351
|
+
return AUDIT_ADDON_PROMPT.handler(args);
|
|
352
|
+
case FIX_NET_EXPLOIT_PROMPT.name:
|
|
353
|
+
return FIX_NET_EXPLOIT_PROMPT.handler(args);
|
|
354
|
+
case SCAFFOLD_SWEP_PROMPT.name:
|
|
355
|
+
return SCAFFOLD_SWEP_PROMPT.handler(args);
|
|
356
|
+
case BUILD_DERMA_MENU_PROMPT.name:
|
|
357
|
+
return BUILD_DERMA_MENU_PROMPT.handler(args);
|
|
358
|
+
default:
|
|
359
|
+
throw new Error(`Prompt not found: ${name}`);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
return server;
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=server.js.map
|