@frontmcp/plugin-codecall 1.8.2 → 1.8.3
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/codecall.symbol.d.ts +14 -14
- package/codecall.symbol.d.ts.map +1 -1
- package/codecall.types.d.ts +28 -5
- package/codecall.types.d.ts.map +1 -1
- package/errors/tool-call.errors.d.ts.map +1 -1
- package/esm/index.mjs +269 -123
- package/esm/package.json +6 -6
- package/index.js +265 -119
- package/package.json +6 -6
- package/security/codecall-tool-policy.d.ts +30 -2
- package/security/codecall-tool-policy.d.ts.map +1 -1
- package/services/audit-logger.service.d.ts.map +1 -1
- package/services/enclave.service.d.ts.map +1 -1
- package/services/output-sanitizer.d.ts.map +1 -1
- package/services/tool-search.service.d.ts +8 -12
- package/services/tool-search.service.d.ts.map +1 -1
- package/tools/describe.tool.d.ts.map +1 -1
- package/tools/execute.schema.d.ts.map +1 -1
- package/tools/execute.tool.d.ts.map +1 -1
- package/tools/invoke.tool.d.ts.map +1 -1
- package/tools/search.tool.d.ts.map +1 -1
- package/utils/build-tool-namespaces.d.ts +27 -20
- package/utils/build-tool-namespaces.d.ts.map +1 -1
- package/utils/client-error-message.d.ts +11 -0
- package/utils/client-error-message.d.ts.map +1 -0
package/esm/index.mjs
CHANGED
|
@@ -374,6 +374,46 @@ function presetDefaults(preset) {
|
|
|
374
374
|
|
|
375
375
|
// plugins/plugin-codecall/src/services/audit-logger.service.ts
|
|
376
376
|
import { Provider as Provider2, ProviderScope as ProviderScope2 } from "@frontmcp/sdk";
|
|
377
|
+
|
|
378
|
+
// plugins/plugin-codecall/src/utils/client-error-message.ts
|
|
379
|
+
var EMBEDDED_STACK_FRAME_RE = /\n[ \t]*at\s[^\n]*/g;
|
|
380
|
+
var QUOTED_PATH_RE = /(['"`])((?:~?\/|[A-Za-z]:[\\/]|\\\\)[^\s'"`\\/][^'"`\n]*)\1/g;
|
|
381
|
+
var URL_RE = /\b[A-Za-z][A-Za-z0-9+.-]{1,31}:\/\/(?:[^\s'"`<>,;]|[,;](?!~?\/|[A-Za-z]:[\\/]|\\\\))*/g;
|
|
382
|
+
var QUERY_PATH_VALUE_RE = /([?&][^=&#]*=)(?:~?\/|[A-Za-z]:[\\/]|\\\\)[^&#]*/g;
|
|
383
|
+
var POSIX_SEGMENT = String.raw`[^\s/\\'"\x60<>|,;:()\[\]{}]+`;
|
|
384
|
+
var WINDOWS_SEGMENT = String.raw`[^\s/\\'"\x60<>|,;:*?]+`;
|
|
385
|
+
function segments(segment, separator) {
|
|
386
|
+
const spaced = `${segment}(?: ${segment}){0,2}`;
|
|
387
|
+
return `(?:(?:${spaced}${separator})+(?:${segment})?|${segment})`;
|
|
388
|
+
}
|
|
389
|
+
var UNQUOTED_PATH_RE = new RegExp(
|
|
390
|
+
[
|
|
391
|
+
String.raw`(?<![\w.~/\\)\]-])~?/+${segments(POSIX_SEGMENT, "/+")}`,
|
|
392
|
+
String.raw`(?<![\w\\/])[A-Za-z]:[\\/]+${segments(WINDOWS_SEGMENT, String.raw`[\\/]+`)}?`,
|
|
393
|
+
String.raw`(?<![\w\\])\\\\${segments(WINDOWS_SEGMENT, String.raw`[\\/]+`)}`
|
|
394
|
+
].join("|"),
|
|
395
|
+
"g"
|
|
396
|
+
);
|
|
397
|
+
function redactPaths(text) {
|
|
398
|
+
return text.replace(QUOTED_PATH_RE, "$1[path]$1").replace(UNQUOTED_PATH_RE, "[path]");
|
|
399
|
+
}
|
|
400
|
+
function toClientErrorMessage(message) {
|
|
401
|
+
if (!message) return "";
|
|
402
|
+
return redactAbsolutePaths(message.replace(EMBEDDED_STACK_FRAME_RE, ""));
|
|
403
|
+
}
|
|
404
|
+
function redactAbsolutePaths(text) {
|
|
405
|
+
let result = "";
|
|
406
|
+
let last = 0;
|
|
407
|
+
for (const match of text.matchAll(URL_RE)) {
|
|
408
|
+
const start = match.index ?? 0;
|
|
409
|
+
result += redactPaths(text.slice(last, start));
|
|
410
|
+
result += /^file:/i.test(match[0]) ? "[path]" : match[0].replace(QUERY_PATH_VALUE_RE, "$1[path]");
|
|
411
|
+
last = start + match[0].length;
|
|
412
|
+
}
|
|
413
|
+
return result + redactPaths(text.slice(last));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// plugins/plugin-codecall/src/services/audit-logger.service.ts
|
|
377
417
|
var AUDIT_EVENT_TYPES = {
|
|
378
418
|
/** Script execution started */
|
|
379
419
|
EXECUTION_START: "codecall:execution:start",
|
|
@@ -650,9 +690,8 @@ var AuditLoggerService = class {
|
|
|
650
690
|
*/
|
|
651
691
|
sanitizeError(error) {
|
|
652
692
|
if (!error) return "Unknown error";
|
|
653
|
-
let sanitized = error
|
|
693
|
+
let sanitized = toClientErrorMessage(error);
|
|
654
694
|
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
655
|
-
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
656
695
|
if (sanitized.length > 200) {
|
|
657
696
|
sanitized = sanitized.substring(0, 200) + "...";
|
|
658
697
|
}
|
|
@@ -671,6 +710,10 @@ import { TFIDFVectoria, VectoriaDB } from "vectoriadb";
|
|
|
671
710
|
|
|
672
711
|
// plugins/plugin-codecall/src/security/codecall-tool-policy.ts
|
|
673
712
|
var BLOCKED_NAMESPACE_PATTERNS = Object.freeze([/^system:/, /^internal:/, /^__/]);
|
|
713
|
+
function isOfferedToCaller(availableWhen, surface) {
|
|
714
|
+
const offered = availableWhen?.surface;
|
|
715
|
+
return offered === void 0 || surface === void 0 || offered.includes(surface);
|
|
716
|
+
}
|
|
674
717
|
var ALLOWED = { allowed: true };
|
|
675
718
|
function deny(reason) {
|
|
676
719
|
return { allowed: false, reason };
|
|
@@ -691,16 +734,46 @@ function toCodeCallPolicyTool(entry, requestedName, scope) {
|
|
|
691
734
|
tags: metadata?.tags,
|
|
692
735
|
// `internal` tools refuse every external tools/call, CodeCall's included, so no surface may offer them.
|
|
693
736
|
hidden: metadata?.hideFromDiscovery === true || metadata?.visibility === "hidden" || metadata?.visibility === "internal",
|
|
694
|
-
codecall: metadata?.codecall
|
|
737
|
+
codecall: metadata?.codecall,
|
|
738
|
+
annotations: metadata?.annotations,
|
|
739
|
+
metadata
|
|
695
740
|
};
|
|
696
741
|
}
|
|
742
|
+
function isPlainData(value) {
|
|
743
|
+
if (Array.isArray(value)) return true;
|
|
744
|
+
const proto = Object.getPrototypeOf(value);
|
|
745
|
+
return proto === Object.prototype || proto === null;
|
|
746
|
+
}
|
|
747
|
+
function readOnlyCopy(value, copies) {
|
|
748
|
+
if (typeof value !== "object" || value === null || !isPlainData(value)) return value;
|
|
749
|
+
const existing = copies.get(value);
|
|
750
|
+
if (existing !== void 0) return existing;
|
|
751
|
+
const copy = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value));
|
|
752
|
+
copies.set(value, copy);
|
|
753
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
754
|
+
if (!Object.prototype.propertyIsEnumerable.call(value, key)) continue;
|
|
755
|
+
Object.defineProperty(copy, key, {
|
|
756
|
+
value: readOnlyCopy(Reflect.get(value, key), copies),
|
|
757
|
+
enumerable: true,
|
|
758
|
+
writable: true,
|
|
759
|
+
configurable: true
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
return Object.freeze(copy);
|
|
763
|
+
}
|
|
697
764
|
function toToolFilterInfo(tool) {
|
|
765
|
+
const copies = /* @__PURE__ */ new WeakMap();
|
|
766
|
+
const annotations = readOnlyCopy(tool.annotations, copies);
|
|
767
|
+
const metadata = tool.metadata ? readOnlyCopy({ ...tool.metadata, ...annotations ? { annotations: tool.annotations } : {} }, copies) : void 0;
|
|
698
768
|
return {
|
|
699
769
|
name: tool.name,
|
|
770
|
+
fullName: tool.fullName,
|
|
700
771
|
appId: tool.appId,
|
|
701
772
|
source: tool.codecall?.source,
|
|
702
773
|
description: tool.description,
|
|
703
|
-
tags: tool.codecall?.tags ?? tool.tags
|
|
774
|
+
tags: readOnlyCopy(tool.codecall?.tags ?? tool.tags, copies),
|
|
775
|
+
annotations,
|
|
776
|
+
metadata
|
|
704
777
|
};
|
|
705
778
|
}
|
|
706
779
|
function ownNamesOf(tool) {
|
|
@@ -787,6 +860,9 @@ function readCodeCallPolicyConfig(config) {
|
|
|
787
860
|
function checkCodeCallToolAccess(scope, config, name, options = {}) {
|
|
788
861
|
const entry = resolveCodeCallTool(scope, name);
|
|
789
862
|
if (!entry) return denyUnknownTool(name);
|
|
863
|
+
if (!isOfferedToCaller(entry.metadata?.availableWhen, options.surface)) {
|
|
864
|
+
return deny(`Tool "${name}" is not offered on the caller's surface "${options.surface}"`);
|
|
865
|
+
}
|
|
790
866
|
const policyTool = toCodeCallPolicyTool(entry, name, scope);
|
|
791
867
|
const baseDecision = checkCodeCallToolPolicy(policyTool, readCodeCallPolicyConfig(config));
|
|
792
868
|
if (!baseDecision.allowed) return baseDecision;
|
|
@@ -1320,6 +1396,8 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1320
1396
|
subscriptionReject = null;
|
|
1321
1397
|
retryTimeoutId = null;
|
|
1322
1398
|
disposed = false;
|
|
1399
|
+
/** `availableWhen` of each indexed tool, by indexed name, for per-caller surface filtering. */
|
|
1400
|
+
availabilityByTool = /* @__PURE__ */ new Map();
|
|
1323
1401
|
constructor(config = {}, scope) {
|
|
1324
1402
|
this.scope = scope;
|
|
1325
1403
|
const embeddingOptions = config.embeddingOptions || {
|
|
@@ -1429,6 +1507,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1429
1507
|
*/
|
|
1430
1508
|
async handleToolChange(tools) {
|
|
1431
1509
|
this.vectorDB.clear();
|
|
1510
|
+
this.availabilityByTool = /* @__PURE__ */ new Map();
|
|
1432
1511
|
if (tools.length === 0) {
|
|
1433
1512
|
this.initialized = true;
|
|
1434
1513
|
return;
|
|
@@ -1443,6 +1522,7 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1443
1522
|
return;
|
|
1444
1523
|
}
|
|
1445
1524
|
const documents = filteredTools.map((tool) => {
|
|
1525
|
+
this.availabilityByTool.set(tool.name, tool.metadata.availableWhen);
|
|
1446
1526
|
const searchableText = this.extractSearchableText(tool);
|
|
1447
1527
|
const appId = this.extractAppId(tool);
|
|
1448
1528
|
const toolName = tool.name;
|
|
@@ -1571,12 +1651,15 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1571
1651
|
*/
|
|
1572
1652
|
async search(query, options = {}) {
|
|
1573
1653
|
await this.ensureSubscribed();
|
|
1574
|
-
const { topK = this.config.defaultTopK, appIds, excludeToolNames = [] } = options;
|
|
1654
|
+
const { topK = this.config.defaultTopK, appIds, excludeToolNames = [], surface } = options;
|
|
1575
1655
|
const minScore = this.config.defaultSimilarityThreshold;
|
|
1576
1656
|
const filter = (metadata) => {
|
|
1577
1657
|
if (excludeToolNames.includes(metadata.toolName)) {
|
|
1578
1658
|
return false;
|
|
1579
1659
|
}
|
|
1660
|
+
if (!isOfferedToCaller(metadata.toolInstance.metadata.availableWhen, surface)) {
|
|
1661
|
+
return false;
|
|
1662
|
+
}
|
|
1580
1663
|
if (appIds && appIds.length > 0) {
|
|
1581
1664
|
if (!metadata.appId || !appIds.includes(metadata.appId)) {
|
|
1582
1665
|
return false;
|
|
@@ -1608,9 +1691,12 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1608
1691
|
}
|
|
1609
1692
|
}
|
|
1610
1693
|
/**
|
|
1611
|
-
* Gets the total number of indexed tools
|
|
1694
|
+
* Gets the total number of indexed tools, or of those a caller on `surface` may reach
|
|
1612
1695
|
*/
|
|
1613
|
-
getTotalCount() {
|
|
1696
|
+
getTotalCount(surface) {
|
|
1697
|
+
if (surface !== void 0) {
|
|
1698
|
+
return this.getAllToolNames().filter((toolName) => this.hasTool(toolName, surface)).length;
|
|
1699
|
+
}
|
|
1614
1700
|
if (this.vectorDB instanceof VectoriaDB) {
|
|
1615
1701
|
return this.vectorDB.size();
|
|
1616
1702
|
} else {
|
|
@@ -1618,20 +1704,18 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1618
1704
|
}
|
|
1619
1705
|
}
|
|
1620
1706
|
/**
|
|
1621
|
-
* Checks if a tool exists in the index
|
|
1707
|
+
* Checks if a tool exists in the index, for a caller on `surface` when given
|
|
1622
1708
|
*/
|
|
1623
|
-
hasTool(toolName) {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
} else {
|
|
1627
|
-
return this.vectorDB.hasDocument(toolName);
|
|
1628
|
-
}
|
|
1709
|
+
hasTool(toolName, surface) {
|
|
1710
|
+
const indexed = this.vectorDB instanceof VectoriaDB ? this.vectorDB.has(toolName) : this.vectorDB.hasDocument(toolName);
|
|
1711
|
+
return indexed && isOfferedToCaller(this.availabilityByTool.get(toolName), surface);
|
|
1629
1712
|
}
|
|
1630
1713
|
/**
|
|
1631
1714
|
* Clears the entire index
|
|
1632
1715
|
*/
|
|
1633
1716
|
clear() {
|
|
1634
1717
|
this.vectorDB.clear();
|
|
1718
|
+
this.availabilityByTool = /* @__PURE__ */ new Map();
|
|
1635
1719
|
this.initialized = false;
|
|
1636
1720
|
}
|
|
1637
1721
|
/**
|
|
@@ -1651,6 +1735,135 @@ var ToolSearchService = class _ToolSearchService {
|
|
|
1651
1735
|
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1652
1736
|
import { Enclave } from "@enclave-vm/core";
|
|
1653
1737
|
import { Provider as Provider3, ProviderScope as ProviderScope3 } from "@frontmcp/sdk";
|
|
1738
|
+
|
|
1739
|
+
// plugins/plugin-codecall/src/utils/build-tool-namespaces.ts
|
|
1740
|
+
var IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
1741
|
+
var PROTOTYPE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1742
|
+
var RESERVED_NAMESPACES = /* @__PURE__ */ new Set([
|
|
1743
|
+
"console",
|
|
1744
|
+
"Math",
|
|
1745
|
+
"JSON",
|
|
1746
|
+
"Object",
|
|
1747
|
+
"Array",
|
|
1748
|
+
"String",
|
|
1749
|
+
"Number",
|
|
1750
|
+
"Boolean",
|
|
1751
|
+
"Promise",
|
|
1752
|
+
"Symbol",
|
|
1753
|
+
"Map",
|
|
1754
|
+
"Set",
|
|
1755
|
+
"WeakMap",
|
|
1756
|
+
"WeakSet",
|
|
1757
|
+
"Date",
|
|
1758
|
+
"Error",
|
|
1759
|
+
"RegExp",
|
|
1760
|
+
"globalThis",
|
|
1761
|
+
"global",
|
|
1762
|
+
"window",
|
|
1763
|
+
"self",
|
|
1764
|
+
"undefined",
|
|
1765
|
+
"null",
|
|
1766
|
+
"true",
|
|
1767
|
+
"false",
|
|
1768
|
+
"callTool",
|
|
1769
|
+
"getTool",
|
|
1770
|
+
"mcpLog",
|
|
1771
|
+
"mcpNotify"
|
|
1772
|
+
]);
|
|
1773
|
+
var UNBINDABLE_NAMESPACES = new Set(
|
|
1774
|
+
[
|
|
1775
|
+
// Reserved words and strict-mode restricted bindings
|
|
1776
|
+
"break case catch class const continue debugger default delete do else enum export extends finally for",
|
|
1777
|
+
"function if import in instanceof new return super switch this throw try typeof var void while with yield",
|
|
1778
|
+
"let static implements interface package private protected public await eval arguments NaN Infinity",
|
|
1779
|
+
// AgentScript globals
|
|
1780
|
+
"parallel isNaN isFinite parseInt parseFloat encodeURI encodeURIComponent decodeURI decodeURIComponent",
|
|
1781
|
+
// Identifiers the AgentScript validator refuses
|
|
1782
|
+
"process require module exports Buffer Function AsyncFunction GeneratorFunction Reflect Proxy TypeError",
|
|
1783
|
+
"ReferenceError SyntaxError RangeError URIError EvalError AggregateError fetch XMLHttpRequest WebSocket",
|
|
1784
|
+
"localStorage sessionStorage indexedDB crypto performance structuredClone AbortController AbortSignal",
|
|
1785
|
+
"MessageChannel MessagePort BroadcastChannel TextEncoder TextDecoder Intl setTimeout setInterval",
|
|
1786
|
+
"setImmediate clearTimeout clearInterval clearImmediate queueMicrotask WebAssembly Worker SharedWorker",
|
|
1787
|
+
"ServiceWorker WeakRef FinalizationRegistry BigInt Atomics SharedArrayBuffer ArrayBuffer DataView"
|
|
1788
|
+
].join(" ").split(" ")
|
|
1789
|
+
);
|
|
1790
|
+
function buildToolNamespaces(tools) {
|
|
1791
|
+
const namespaces = /* @__PURE__ */ Object.create(null);
|
|
1792
|
+
const skipped = [];
|
|
1793
|
+
for (const tool of tools) {
|
|
1794
|
+
const name = tool?.name;
|
|
1795
|
+
if (typeof name !== "string" || name.length === 0) continue;
|
|
1796
|
+
const dot = name.indexOf(".");
|
|
1797
|
+
if (dot <= 0 || dot === name.length - 1) {
|
|
1798
|
+
skipped.push({ name, reason: "no-namespace-prefix" });
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
const ns = name.slice(0, dot);
|
|
1802
|
+
const method = name.slice(dot + 1);
|
|
1803
|
+
if (!IDENT_RE.test(ns) || !IDENT_RE.test(method)) {
|
|
1804
|
+
skipped.push({ name, reason: "invalid-identifier" });
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
if (PROTOTYPE_KEYS.has(ns) || PROTOTYPE_KEYS.has(method)) {
|
|
1808
|
+
skipped.push({ name, reason: "prototype-key" });
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
if (RESERVED_NAMESPACES.has(ns) || UNBINDABLE_NAMESPACES.has(ns) || ns.startsWith("__")) {
|
|
1812
|
+
skipped.push({ name, reason: "reserved-namespace" });
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
const bucket = namespaces[ns] ?? (namespaces[ns] = /* @__PURE__ */ Object.create(null));
|
|
1816
|
+
if (Object.prototype.hasOwnProperty.call(bucket, method)) {
|
|
1817
|
+
skipped.push({ name, reason: "duplicate-method" });
|
|
1818
|
+
continue;
|
|
1819
|
+
}
|
|
1820
|
+
bucket[method] = name;
|
|
1821
|
+
}
|
|
1822
|
+
return { namespaces, skipped };
|
|
1823
|
+
}
|
|
1824
|
+
var NAMESPACE_CALL_HELPER = "__codecallNamespaceCall";
|
|
1825
|
+
var NAMESPACE_CALL_HELPER_SOURCE = `const ${NAMESPACE_CALL_HELPER} = async (toolName, run, options) => { if (options && options.throwOnError === false) { try { return { success: true, data: await run() }; } catch (error) { return { success: false, error: { message: error && error.message, toolName: toolName } }; } } return await run(); };`;
|
|
1826
|
+
function mentionsIdentifier(script, identifier) {
|
|
1827
|
+
let from = 0;
|
|
1828
|
+
for (; ; ) {
|
|
1829
|
+
const at = script.indexOf(identifier, from);
|
|
1830
|
+
if (at === -1) return false;
|
|
1831
|
+
from = at + 1;
|
|
1832
|
+
const before = at > 0 ? script[at - 1] : "";
|
|
1833
|
+
const after = script[at + identifier.length] ?? "";
|
|
1834
|
+
if (/[A-Za-z0-9_$'"`]/.test(before) || /[A-Za-z0-9_$]/.test(after)) continue;
|
|
1835
|
+
if (before === "." && script.slice(Math.max(0, at - 3), at) !== "...") continue;
|
|
1836
|
+
return true;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
function wrapScriptWithToolNamespaces(script, namespaces) {
|
|
1840
|
+
if (!namespaces) return script;
|
|
1841
|
+
const declarations = [];
|
|
1842
|
+
for (const ns of Object.keys(namespaces)) {
|
|
1843
|
+
if (!IDENT_RE.test(ns) || RESERVED_NAMESPACES.has(ns) || UNBINDABLE_NAMESPACES.has(ns) || ns.startsWith("__")) {
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
if (PROTOTYPE_KEYS.has(ns) || !mentionsIdentifier(script, ns)) continue;
|
|
1847
|
+
const methods = namespaces[ns];
|
|
1848
|
+
const entries = [];
|
|
1849
|
+
for (const method of Object.keys(methods)) {
|
|
1850
|
+
const toolName = methods[method];
|
|
1851
|
+
if (!IDENT_RE.test(method) || PROTOTYPE_KEYS.has(method) || toolName !== `${ns}.${method}`) continue;
|
|
1852
|
+
const literal = JSON.stringify(toolName);
|
|
1853
|
+
entries.push(
|
|
1854
|
+
`${JSON.stringify(method)}: (input, options) => ${NAMESPACE_CALL_HELPER}(${literal}, () => callTool(${literal}, input === undefined ? {} : input), options)`
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
if (entries.length > 0) {
|
|
1858
|
+
declarations.push(`const ${ns} = { ${entries.join(", ")} };`);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (declarations.length === 0) return script;
|
|
1862
|
+
return `${NAMESPACE_CALL_HELPER_SOURCE} ${declarations.join(" ")} return await (async () => {${script}
|
|
1863
|
+
})();`;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// plugins/plugin-codecall/src/services/enclave.service.ts
|
|
1654
1867
|
var ScriptTooLargeError = class extends Error {
|
|
1655
1868
|
code = "SCRIPT_TOO_LARGE";
|
|
1656
1869
|
scriptLength;
|
|
@@ -1699,10 +1912,7 @@ var EnclaveService = class {
|
|
|
1699
1912
|
maxResolvedSize: this.sidecarOptions.maxResolvedSize,
|
|
1700
1913
|
allowComposites: this.sidecarOptions.allowComposites
|
|
1701
1914
|
} : void 0;
|
|
1702
|
-
const
|
|
1703
|
-
const safeNamespaces = Object.fromEntries(
|
|
1704
|
-
Object.entries(environment.namespaces ?? {}).filter(([key]) => !RESERVED_GLOBALS.has(key))
|
|
1705
|
-
);
|
|
1915
|
+
const source = wrapScriptWithToolNamespaces(code, environment.toolNamespaces);
|
|
1706
1916
|
const enclave = new Enclave({
|
|
1707
1917
|
timeout: this.vmOptions.timeoutMs,
|
|
1708
1918
|
maxToolCalls: this.vmOptions.maxSteps || 100,
|
|
@@ -1716,11 +1926,6 @@ var EnclaveService = class {
|
|
|
1716
1926
|
// Allow functions in globals since we intentionally provide getTool, mcpLog, mcpNotify, and console
|
|
1717
1927
|
allowFunctionsInGlobals: true,
|
|
1718
1928
|
globals: {
|
|
1719
|
-
// Tool namespaces (e.g. `acme.getUser`) flow through here. Spread
|
|
1720
|
-
// FIRST so the runtime reserved globals below are guaranteed
|
|
1721
|
-
// last-wins. Reserved keys have already been stripped above —
|
|
1722
|
-
// belt-and-braces ordering makes the invariant locally obvious.
|
|
1723
|
-
...safeNamespaces,
|
|
1724
1929
|
// Provide getTool as a custom global
|
|
1725
1930
|
getTool: environment.getTool,
|
|
1726
1931
|
// Provide logging functions if available
|
|
@@ -1743,7 +1948,7 @@ var EnclaveService = class {
|
|
|
1743
1948
|
}
|
|
1744
1949
|
});
|
|
1745
1950
|
try {
|
|
1746
|
-
const result = await enclave.run(
|
|
1951
|
+
const result = await enclave.run(source);
|
|
1747
1952
|
return this.mapEnclaveResult(result, logs);
|
|
1748
1953
|
} finally {
|
|
1749
1954
|
enclave.dispose();
|
|
@@ -1833,7 +2038,7 @@ EnclaveService = __decorateClass([
|
|
|
1833
2038
|
], EnclaveService);
|
|
1834
2039
|
|
|
1835
2040
|
// plugins/plugin-codecall/src/tools/search.tool.ts
|
|
1836
|
-
import { Tool, ToolContext } from "@frontmcp/sdk";
|
|
2041
|
+
import { getCallSurface, Tool, ToolContext } from "@frontmcp/sdk";
|
|
1837
2042
|
|
|
1838
2043
|
// plugins/plugin-codecall/src/tools/search.schema.ts
|
|
1839
2044
|
import { z as z2 } from "@frontmcp/lazy-zod";
|
|
@@ -1891,7 +2096,10 @@ var SearchTool = class extends ToolContext {
|
|
|
1891
2096
|
const audit = this.tryGet(AuditLoggerService);
|
|
1892
2097
|
const executionId = audit ? audit.generateExecutionId() : "";
|
|
1893
2098
|
const startedAt = Date.now();
|
|
1894
|
-
const
|
|
2099
|
+
const surface = getCallSurface();
|
|
2100
|
+
const nonExistentExcludedTools = excludeToolNames.filter(
|
|
2101
|
+
(toolName) => !searchService.hasTool(toolName, surface)
|
|
2102
|
+
);
|
|
1895
2103
|
if (nonExistentExcludedTools.length > 0) {
|
|
1896
2104
|
warnings.push({
|
|
1897
2105
|
type: "excluded_tool_not_found",
|
|
@@ -1905,7 +2113,8 @@ var SearchTool = class extends ToolContext {
|
|
|
1905
2113
|
const searchResults = await searchService.search(query, {
|
|
1906
2114
|
topK,
|
|
1907
2115
|
appIds,
|
|
1908
|
-
excludeToolNames
|
|
2116
|
+
excludeToolNames,
|
|
2117
|
+
surface
|
|
1909
2118
|
});
|
|
1910
2119
|
for (const result of searchResults) {
|
|
1911
2120
|
if (result.relevanceScore < minRelevanceScore) {
|
|
@@ -1950,7 +2159,7 @@ var SearchTool = class extends ToolContext {
|
|
|
1950
2159
|
return {
|
|
1951
2160
|
tools,
|
|
1952
2161
|
warnings,
|
|
1953
|
-
totalAvailableTools: searchService.getTotalCount()
|
|
2162
|
+
totalAvailableTools: searchService.getTotalCount(surface)
|
|
1954
2163
|
};
|
|
1955
2164
|
}
|
|
1956
2165
|
};
|
|
@@ -2263,7 +2472,7 @@ SearchKnowledgeTool = __decorateClass([
|
|
|
2263
2472
|
|
|
2264
2473
|
// plugins/plugin-codecall/src/tools/describe.tool.ts
|
|
2265
2474
|
import { toJSONSchema, z as z6, ZodType } from "@frontmcp/lazy-zod";
|
|
2266
|
-
import { Tool as Tool4, ToolContext as ToolContext4 } from "@frontmcp/sdk";
|
|
2475
|
+
import { getCallSurface as getCallSurface2, Tool as Tool4, ToolContext as ToolContext4 } from "@frontmcp/sdk";
|
|
2267
2476
|
|
|
2268
2477
|
// plugins/plugin-codecall/src/errors/tool-call.errors.ts
|
|
2269
2478
|
var TOOL_CALL_ERROR_CODES = {
|
|
@@ -2301,11 +2510,10 @@ function getSanitizedMessage(code, toolName, rawMessage) {
|
|
|
2301
2510
|
}
|
|
2302
2511
|
}
|
|
2303
2512
|
function sanitizeValidationMessage(message) {
|
|
2304
|
-
let sanitized = message
|
|
2513
|
+
let sanitized = toClientErrorMessage(message);
|
|
2305
2514
|
sanitized = sanitized.replace(/\bat line \d+/gi, "");
|
|
2306
2515
|
sanitized = sanitized.replace(/\bline \d+/gi, "");
|
|
2307
2516
|
sanitized = sanitized.replace(/:\d+:\d+/g, "");
|
|
2308
|
-
sanitized = sanitized.replace(/\n\s*at .*/g, "");
|
|
2309
2517
|
if (sanitized.length > 200) {
|
|
2310
2518
|
sanitized = sanitized.substring(0, 200) + "...";
|
|
2311
2519
|
}
|
|
@@ -2343,75 +2551,6 @@ function assertNotSelfReference(toolName) {
|
|
|
2343
2551
|
}
|
|
2344
2552
|
}
|
|
2345
2553
|
|
|
2346
|
-
// plugins/plugin-codecall/src/utils/build-tool-namespaces.ts
|
|
2347
|
-
var IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
2348
|
-
var PROTOTYPE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
2349
|
-
var RESERVED_NAMESPACES = /* @__PURE__ */ new Set([
|
|
2350
|
-
"console",
|
|
2351
|
-
"Math",
|
|
2352
|
-
"JSON",
|
|
2353
|
-
"Object",
|
|
2354
|
-
"Array",
|
|
2355
|
-
"String",
|
|
2356
|
-
"Number",
|
|
2357
|
-
"Boolean",
|
|
2358
|
-
"Promise",
|
|
2359
|
-
"Symbol",
|
|
2360
|
-
"Map",
|
|
2361
|
-
"Set",
|
|
2362
|
-
"WeakMap",
|
|
2363
|
-
"WeakSet",
|
|
2364
|
-
"Date",
|
|
2365
|
-
"Error",
|
|
2366
|
-
"RegExp",
|
|
2367
|
-
"globalThis",
|
|
2368
|
-
"global",
|
|
2369
|
-
"window",
|
|
2370
|
-
"self",
|
|
2371
|
-
"undefined",
|
|
2372
|
-
"null",
|
|
2373
|
-
"true",
|
|
2374
|
-
"false",
|
|
2375
|
-
"callTool",
|
|
2376
|
-
"getTool",
|
|
2377
|
-
"mcpLog",
|
|
2378
|
-
"mcpNotify"
|
|
2379
|
-
]);
|
|
2380
|
-
function buildToolNamespaces(tools, callTool) {
|
|
2381
|
-
const namespaces = /* @__PURE__ */ Object.create(null);
|
|
2382
|
-
const skipped = [];
|
|
2383
|
-
for (const tool of tools) {
|
|
2384
|
-
const name = tool?.name;
|
|
2385
|
-
if (typeof name !== "string" || name.length === 0) continue;
|
|
2386
|
-
const dot = name.indexOf(".");
|
|
2387
|
-
if (dot <= 0 || dot === name.length - 1) {
|
|
2388
|
-
skipped.push({ name, reason: "no-namespace-prefix" });
|
|
2389
|
-
continue;
|
|
2390
|
-
}
|
|
2391
|
-
const ns = name.slice(0, dot);
|
|
2392
|
-
const method = name.slice(dot + 1);
|
|
2393
|
-
if (!IDENT_RE.test(ns) || !IDENT_RE.test(method)) {
|
|
2394
|
-
skipped.push({ name, reason: "invalid-identifier" });
|
|
2395
|
-
continue;
|
|
2396
|
-
}
|
|
2397
|
-
if (PROTOTYPE_KEYS.has(ns)) {
|
|
2398
|
-
skipped.push({ name, reason: "prototype-key" });
|
|
2399
|
-
continue;
|
|
2400
|
-
}
|
|
2401
|
-
if (RESERVED_NAMESPACES.has(ns)) {
|
|
2402
|
-
skipped.push({ name, reason: "reserved-namespace" });
|
|
2403
|
-
continue;
|
|
2404
|
-
}
|
|
2405
|
-
const bucket = namespaces[ns] ?? (namespaces[ns] = /* @__PURE__ */ Object.create(null));
|
|
2406
|
-
if (Object.prototype.hasOwnProperty.call(bucket, method)) {
|
|
2407
|
-
skipped.push({ name, reason: "duplicate-method" });
|
|
2408
|
-
continue;
|
|
2409
|
-
}
|
|
2410
|
-
bucket[method] = (input, options) => callTool(name, input, options);
|
|
2411
|
-
}
|
|
2412
|
-
return { namespaces, skipped };
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
2554
|
// plugins/plugin-codecall/src/utils/describe.utils.ts
|
|
2416
2555
|
var INTENT_PATTERNS = {
|
|
2417
2556
|
create: /^(create|add|new|insert|make|append|register|generate|produce|build|construct|provision|instantiate|define|compose|draft)/i,
|
|
@@ -2773,12 +2912,13 @@ var DescribeTool = class extends ToolContext4 {
|
|
|
2773
2912
|
const tools = [];
|
|
2774
2913
|
const notFound = [];
|
|
2775
2914
|
const config = this.get(CodeCallConfig);
|
|
2915
|
+
const surface = getCallSurface2();
|
|
2776
2916
|
for (const toolName of toolNames) {
|
|
2777
2917
|
if (isBlockedSelfReference(toolName)) {
|
|
2778
2918
|
notFound.push(toolName);
|
|
2779
2919
|
continue;
|
|
2780
2920
|
}
|
|
2781
|
-
const access = checkCodeCallToolAccess(this.scope, config, toolName);
|
|
2921
|
+
const access = checkCodeCallToolAccess(this.scope, config, toolName, { surface });
|
|
2782
2922
|
if (!access.allowed) {
|
|
2783
2923
|
audit?.logSecurityAccessDenied(executionId, toolName, access.reason);
|
|
2784
2924
|
notFound.push(toolName);
|
|
@@ -2936,7 +3076,7 @@ DescribeTool = __decorateClass([
|
|
|
2936
3076
|
], DescribeTool);
|
|
2937
3077
|
|
|
2938
3078
|
// plugins/plugin-codecall/src/tools/execute.tool.ts
|
|
2939
|
-
import { Tool as Tool5, ToolContext as ToolContext5 } from "@frontmcp/sdk";
|
|
3079
|
+
import { getCallSurface as getCallSurface3, Tool as Tool5, ToolContext as ToolContext5 } from "@frontmcp/sdk";
|
|
2940
3080
|
|
|
2941
3081
|
// plugins/plugin-codecall/src/tools/execute.schema.ts
|
|
2942
3082
|
import { z as z7 } from "@frontmcp/lazy-zod";
|
|
@@ -2990,6 +3130,7 @@ var runtimeErrorPayloadSchema = z7.object({
|
|
|
2990
3130
|
source: z7.literal("script"),
|
|
2991
3131
|
message: z7.string(),
|
|
2992
3132
|
name: z7.string().optional(),
|
|
3133
|
+
/** @deprecated Never set: stack traces name server files, so results don't carry them. */
|
|
2993
3134
|
stack: z7.string().optional()
|
|
2994
3135
|
});
|
|
2995
3136
|
var toolErrorPayloadSchema = z7.object({
|
|
@@ -2998,6 +3139,7 @@ var toolErrorPayloadSchema = z7.object({
|
|
|
2998
3139
|
toolInput: z7.unknown(),
|
|
2999
3140
|
message: z7.string(),
|
|
3000
3141
|
code: z7.string().optional(),
|
|
3142
|
+
/** @deprecated Never set: the enclave's error data is not sanitized for clients. */
|
|
3001
3143
|
details: z7.unknown().optional()
|
|
3002
3144
|
});
|
|
3003
3145
|
var timeoutErrorPayloadSchema = z7.object({
|
|
@@ -3062,11 +3204,12 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3062
3204
|
* otherwise it would skip every metadata-driven check and reach `tools:call-tool`
|
|
3063
3205
|
* unexamined.
|
|
3064
3206
|
*/
|
|
3065
|
-
checkToolPolicy(name) {
|
|
3066
|
-
return checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), name);
|
|
3207
|
+
checkToolPolicy(name, surface) {
|
|
3208
|
+
return checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), name, { surface });
|
|
3067
3209
|
}
|
|
3068
3210
|
async execute(input) {
|
|
3069
3211
|
const { script, allowedTools } = input;
|
|
3212
|
+
const callerSurface = getCallSurface3();
|
|
3070
3213
|
const audit = this.tryGet(AuditLoggerService);
|
|
3071
3214
|
const executionId = audit ? audit.generateExecutionId() : "";
|
|
3072
3215
|
const callDepth = 1;
|
|
@@ -3079,7 +3222,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3079
3222
|
audit?.logSecuritySelfReference(executionId, name);
|
|
3080
3223
|
}
|
|
3081
3224
|
assertNotSelfReference(name);
|
|
3082
|
-
const decision = this.checkToolPolicy(name);
|
|
3225
|
+
const decision = this.checkToolPolicy(name, callerSurface);
|
|
3083
3226
|
if (!decision.allowed) {
|
|
3084
3227
|
audit?.logSecurityAccessDenied(executionId, name, decision.reason);
|
|
3085
3228
|
const error = createToolCallError(TOOL_CALL_ERROR_CODES.ACCESS_DENIED, name, decision.reason);
|
|
@@ -3108,7 +3251,8 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3108
3251
|
}
|
|
3109
3252
|
};
|
|
3110
3253
|
const ctx = {
|
|
3111
|
-
authInfo: this.authInfo
|
|
3254
|
+
authInfo: this.authInfo,
|
|
3255
|
+
surface: callerSurface
|
|
3112
3256
|
};
|
|
3113
3257
|
const mcpResult = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
3114
3258
|
if (!mcpResult) {
|
|
@@ -3148,7 +3292,7 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3148
3292
|
audit?.logSecuritySelfReference(executionId, name);
|
|
3149
3293
|
return void 0;
|
|
3150
3294
|
}
|
|
3151
|
-
const introspectionAccess = this.checkToolPolicy(name);
|
|
3295
|
+
const introspectionAccess = this.checkToolPolicy(name, callerSurface);
|
|
3152
3296
|
if (!introspectionAccess.allowed) {
|
|
3153
3297
|
audit?.logSecurityAccessDenied(executionId, name, introspectionAccess.reason);
|
|
3154
3298
|
return void 0;
|
|
@@ -3178,8 +3322,8 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3178
3322
|
const callableTools = this.scope.tools.getTools(true).filter(
|
|
3179
3323
|
(tool) => checkCodeCallToolPolicy(toCodeCallPolicyTool(tool, void 0, this.scope), policyConfig).allowed
|
|
3180
3324
|
);
|
|
3181
|
-
const { namespaces, skipped } = buildToolNamespaces(callableTools
|
|
3182
|
-
environment.
|
|
3325
|
+
const { namespaces, skipped } = buildToolNamespaces(callableTools);
|
|
3326
|
+
environment.toolNamespaces = namespaces;
|
|
3183
3327
|
if (skipped.length > 0) {
|
|
3184
3328
|
this.logger?.debug?.("codecall: tools skipped during namespace generation", {
|
|
3185
3329
|
count: skipped.length,
|
|
@@ -3230,9 +3374,9 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3230
3374
|
source: "tool",
|
|
3231
3375
|
toolName: error.toolName,
|
|
3232
3376
|
toolInput: error.toolInput,
|
|
3233
|
-
message: error.message,
|
|
3234
|
-
code: error.code
|
|
3235
|
-
details
|
|
3377
|
+
message: toClientErrorMessage(error.message),
|
|
3378
|
+
code: error.code
|
|
3379
|
+
// No `details`: the enclave's error data is not sanitized for clients.
|
|
3236
3380
|
}
|
|
3237
3381
|
};
|
|
3238
3382
|
}
|
|
@@ -3241,9 +3385,8 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3241
3385
|
status: "runtime_error",
|
|
3242
3386
|
error: {
|
|
3243
3387
|
source: "script",
|
|
3244
|
-
message: error.message,
|
|
3245
|
-
name: error.name
|
|
3246
|
-
stack: error.stack
|
|
3388
|
+
message: toClientErrorMessage(error.message),
|
|
3389
|
+
name: error.name
|
|
3247
3390
|
}
|
|
3248
3391
|
};
|
|
3249
3392
|
}
|
|
@@ -3257,13 +3400,12 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3257
3400
|
const errorName = error instanceof Error ? error.name : "Error";
|
|
3258
3401
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3259
3402
|
audit?.logExecutionFailure(executionId, script, elapsed(), errorMessage);
|
|
3260
|
-
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
3261
3403
|
const errorLoc = error.loc;
|
|
3262
3404
|
if (errorName === "SyntaxError" || errorMessage?.includes("syntax")) {
|
|
3263
3405
|
return {
|
|
3264
3406
|
status: "syntax_error",
|
|
3265
3407
|
error: {
|
|
3266
|
-
message: errorMessage || "Syntax error in script",
|
|
3408
|
+
message: toClientErrorMessage(errorMessage) || "Syntax error in script",
|
|
3267
3409
|
location: errorLoc ? { line: errorLoc.line, column: errorLoc.column } : void 0
|
|
3268
3410
|
}
|
|
3269
3411
|
};
|
|
@@ -3272,9 +3414,8 @@ var ExecuteTool = class extends ToolContext5 {
|
|
|
3272
3414
|
status: "runtime_error",
|
|
3273
3415
|
error: {
|
|
3274
3416
|
source: "script",
|
|
3275
|
-
message: errorMessage || "An unexpected error occurred during script execution",
|
|
3276
|
-
name: errorName
|
|
3277
|
-
stack: errorStack
|
|
3417
|
+
message: toClientErrorMessage(errorMessage) || "An unexpected error occurred during script execution",
|
|
3418
|
+
name: errorName
|
|
3278
3419
|
}
|
|
3279
3420
|
};
|
|
3280
3421
|
}
|
|
@@ -3299,7 +3440,7 @@ ExecuteTool = __decorateClass([
|
|
|
3299
3440
|
], ExecuteTool);
|
|
3300
3441
|
|
|
3301
3442
|
// plugins/plugin-codecall/src/tools/invoke.tool.ts
|
|
3302
|
-
import { Tool as Tool6, ToolContext as ToolContext6 } from "@frontmcp/sdk";
|
|
3443
|
+
import { getCallSurface as getCallSurface4, Tool as Tool6, ToolContext as ToolContext6 } from "@frontmcp/sdk";
|
|
3303
3444
|
|
|
3304
3445
|
// plugins/plugin-codecall/src/tools/invoke.schema.ts
|
|
3305
3446
|
import { z as z8 } from "@frontmcp/lazy-zod";
|
|
@@ -3343,7 +3484,11 @@ var InvokeTool = class extends ToolContext6 {
|
|
|
3343
3484
|
`Tool "${toolName}" cannot be invoked directly. CodeCall tools are internal and not accessible via codecall:invoke.`
|
|
3344
3485
|
);
|
|
3345
3486
|
}
|
|
3346
|
-
const
|
|
3487
|
+
const surface = getCallSurface4();
|
|
3488
|
+
const decision = checkCodeCallToolAccess(this.scope, this.get(CodeCallConfig), toolName, {
|
|
3489
|
+
directCall: true,
|
|
3490
|
+
surface
|
|
3491
|
+
});
|
|
3347
3492
|
if (!decision.allowed) {
|
|
3348
3493
|
audit?.logSecurityAccessDenied(executionId, toolName, decision.reason);
|
|
3349
3494
|
return buildErrorResult(`Tool "${toolName}" is not available. Use codecall:search to discover available tools.`);
|
|
@@ -3356,7 +3501,8 @@ var InvokeTool = class extends ToolContext6 {
|
|
|
3356
3501
|
}
|
|
3357
3502
|
};
|
|
3358
3503
|
const ctx = {
|
|
3359
|
-
authInfo: this.authInfo
|
|
3504
|
+
authInfo: this.authInfo,
|
|
3505
|
+
surface
|
|
3360
3506
|
};
|
|
3361
3507
|
const result = await this.scope.runFlow("tools:call-tool", { request, ctx });
|
|
3362
3508
|
if (!result) {
|