@frontmcp/plugin-skilled-openapi 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/esm/index.mjs CHANGED
@@ -11,12 +11,14 @@ var __decorateClass = (decorators, target, key2, kind) => {
11
11
 
12
12
  // plugins/plugin-skilled-openapi/src/skilled-openapi.plugin.ts
13
13
  import {
14
- BundleStore,
14
+ BundleStore as BundleStore3,
15
15
  createBundleSource
16
16
  } from "@frontmcp/adapters/skills";
17
17
  import {
18
18
  buildSkillsCatalogSummary,
19
19
  DynamicPlugin,
20
+ FlowHooksOf,
21
+ FrontMcpContextStorage,
20
22
  FrontMcpLogger,
21
23
  ListToolsHook,
22
24
  Plugin,
@@ -122,16 +124,61 @@ import {
122
124
  AuthoritiesEvaluatorRegistry,
123
125
  AuthoritiesProfileRegistry
124
126
  } from "@frontmcp/auth";
127
+ function policyDependsOnInput(value) {
128
+ if (typeof value === "string") return value === "input" || value.startsWith("input.");
129
+ if (Array.isArray(value)) return value.some(policyDependsOnInput);
130
+ if (value !== null && typeof value === "object") {
131
+ for (const [key2, nested] of Object.entries(value)) {
132
+ if (key2 === "fromInput" || key2 === "custom" || key2 === "guards") return true;
133
+ if (policyDependsOnInput(key2) || policyDependsOnInput(nested)) return true;
134
+ }
135
+ }
136
+ return false;
137
+ }
138
+ function judgeablePart(policy, engine) {
139
+ if (policy === void 0 || policy === null) return void 0;
140
+ const profileDependsOnInput = (name) => typeof name === "string" && policyDependsOnInput(engine.resolveProfile(name));
141
+ const value = policy;
142
+ if (typeof value === "string") return profileDependsOnInput(value) ? void 0 : policy;
143
+ if (Array.isArray(value)) {
144
+ const names = value.filter((name) => !profileDependsOnInput(name));
145
+ return names.length > 0 ? names : void 0;
146
+ }
147
+ return policyDependsOnInput(policy) ? void 0 : policy;
148
+ }
125
149
  var AuthorityGuard = class {
126
150
  engine;
127
151
  contextBuilder;
128
152
  logger;
153
+ serverAuthorities;
129
154
  constructor(opts = {}) {
130
155
  const profiles = opts.profiles ?? new AuthoritiesProfileRegistry();
131
156
  const evaluators = opts.evaluators ?? new AuthoritiesEvaluatorRegistry();
132
157
  this.engine = new AuthoritiesEngine(profiles, evaluators);
133
158
  this.contextBuilder = new AuthoritiesContextBuilder();
134
159
  this.logger = opts.logger;
160
+ this.serverAuthorities = opts.serverAuthorities;
161
+ }
162
+ /** The server's engine and context builder when it configures authorities, else the guard's own. */
163
+ authorities() {
164
+ return this.serverAuthorities?.() ?? { engine: this.engine, contextBuilder: this.contextBuilder };
165
+ }
166
+ /**
167
+ * Whether the caller may be shown a skill or action (in `search_skill`,
168
+ * `load_skill`, the catalog and the SDK skill surfaces), judged without the
169
+ * action's input. A rule that depends on the input can't be judged yet and
170
+ * doesn't hide anything; the call is still checked by {@link check}. Every other
171
+ * rule must grant, and a policy-less action under `unprotectedOps: 'deny'` must
172
+ * be public, exactly as at call time.
173
+ */
174
+ async canDiscover(args) {
175
+ const { engine } = this.authorities();
176
+ const hasPolicy = [args.skillPolicy, args.policy].some((p) => p !== void 0 && p !== null);
177
+ const skillPolicy = judgeablePart(args.skillPolicy, engine);
178
+ const policy = judgeablePart(args.policy, engine);
179
+ if (hasPolicy && skillPolicy === void 0 && policy === void 0) return true;
180
+ const result = await this.check({ ...args, skillPolicy, policy, input: {} });
181
+ return result.granted;
135
182
  }
136
183
  async check(args) {
137
184
  const { policy, skillPolicy, isPublic, authInfo, input, env } = args;
@@ -149,10 +196,11 @@ var AuthorityGuard = class {
149
196
  return { granted: true, evaluatedPolicies: [] };
150
197
  }
151
198
  try {
152
- const ctx = this.contextBuilder.build(authInfo, input, env);
199
+ const { engine, contextBuilder } = this.authorities();
200
+ const ctx = contextBuilder.build(authInfo, input, env);
153
201
  const evaluatedPolicies = [];
154
202
  for (const p of policies) {
155
- const res = await this.engine.evaluate(p, ctx);
203
+ const res = await engine.evaluate(p, ctx);
156
204
  evaluatedPolicies.push(...res.evaluatedPolicies ?? []);
157
205
  if (!res.granted) {
158
206
  return { ...res, evaluatedPolicies };
@@ -180,6 +228,112 @@ function normalizeCaughtMessage(e) {
180
228
  return String(e ?? "authority evaluation threw");
181
229
  }
182
230
 
231
+ // plugins/plugin-skilled-openapi/src/security/skill-visibility.ts
232
+ import { filterServableSkills } from "@frontmcp/sdk";
233
+ function bundleSkillPolicies(bundle) {
234
+ const policies = /* @__PURE__ */ new Map();
235
+ for (const skill of bundle?.skills ?? []) {
236
+ if (skill.requiredAuthorities !== void 0 && skill.requiredAuthorities !== null) {
237
+ policies.set(skill.id, skill.requiredAuthorities);
238
+ }
239
+ }
240
+ return policies;
241
+ }
242
+ var SkillVisibility = class {
243
+ constructor(deps) {
244
+ this.deps = deps;
245
+ this.policies = bundleSkillPolicies(deps.bundle);
246
+ this.authInfo = deps.authInfo ?? {};
247
+ }
248
+ deps;
249
+ policies;
250
+ authInfo;
251
+ /** Whether the caller satisfies the bundle skill's own `requiredAuthorities` (true for other skills). */
252
+ async passesBundleSkillRule(skillId) {
253
+ const skillPolicy = this.policies.get(skillId);
254
+ if (skillPolicy === void 0) return true;
255
+ return this.deps.guard.canDiscover({ skillPolicy, authInfo: this.authInfo });
256
+ }
257
+ /**
258
+ * Keep the items whose skill the caller may see: the bundle skill's rule, the
259
+ * `skills:filter` flow and the server's `@Skill({ authorities })` rule. An item
260
+ * whose id names no registered skill (an external skill) is judged by the bundle
261
+ * rule alone, as the SDK's own surfaces keep such results.
262
+ */
263
+ async filterVisible(items, idOf) {
264
+ if (items.length === 0) return [];
265
+ const entries = items.map((item) => this.resolveEntry(idOf(item)));
266
+ const registered = [...new Set(entries.filter((entry) => entry !== void 0))];
267
+ const visibleEntries = new Set(await this.filterVisibleEntries(registered));
268
+ const visible = [];
269
+ for (const [index, item] of items.entries()) {
270
+ const entry = entries[index];
271
+ if (entry ? !visibleEntries.has(entry) : !await this.passesBundleSkillRule(idOf(item))) continue;
272
+ visible.push(item);
273
+ }
274
+ return visible;
275
+ }
276
+ /** The registered skills the caller may see, in their order (see {@link filterVisible}). */
277
+ async filterVisibleEntries(entries) {
278
+ if (entries.length === 0) return [];
279
+ const servable = new Set(await filterServableSkills(this.deps.scope, entries));
280
+ const visible = [];
281
+ for (const entry of entries) {
282
+ if (!servable.has(entry) || !await this.passesSkillAuthorities(entry)) continue;
283
+ if (!await this.passesBundleSkillRule(entry.metadata.id ?? entry.name)) continue;
284
+ visible.push(entry);
285
+ }
286
+ return visible;
287
+ }
288
+ /** Whether the caller may see the skill named by `skillId` (see {@link filterVisible}). */
289
+ async isVisible(skillId) {
290
+ return (await this.filterVisible([skillId], (id) => id)).length === 1;
291
+ }
292
+ /**
293
+ * The actions of a bundle skill the caller may be shown: an action whose rules
294
+ * refuse the caller whatever the input (or that `unprotectedOps: 'deny'` blocks)
295
+ * is left out. Rules that depend on the input keep the action listed; the call is
296
+ * checked again when it runs.
297
+ */
298
+ async visibleActions(skillId, actions, hiddenOps) {
299
+ const visible = [];
300
+ for (const action of actions) {
301
+ const entry = hiddenOps.get(skillId, action.actionId);
302
+ if (!entry) continue;
303
+ const allowed = await this.deps.guard.canDiscover({
304
+ policy: entry.op.requiredAuthorities,
305
+ skillPolicy: entry.skillRequiredAuthorities,
306
+ isPublic: entry.op.public,
307
+ unprotectedOps: this.deps.unprotectedOps,
308
+ authInfo: this.authInfo
309
+ });
310
+ if (allowed) visible.push(action);
311
+ }
312
+ return visible;
313
+ }
314
+ /** The server's `@Skill({ authorities })` rule, evaluated as the SDK's skill surfaces do. */
315
+ async passesSkillAuthorities(entry) {
316
+ const { authoritiesEngine: engine, authoritiesContextBuilder: contextBuilder } = this.deps.scope;
317
+ const authorities = entry.metadata["authorities"];
318
+ if (!engine || !contextBuilder || !authorities) return true;
319
+ try {
320
+ const result = await engine.evaluate(
321
+ authorities,
322
+ contextBuilder.build(this.authInfo)
323
+ );
324
+ return result.granted;
325
+ } catch {
326
+ return false;
327
+ }
328
+ }
329
+ /** The registered skill an id names, looked up the way the SDK's skill surfaces do. */
330
+ resolveEntry(id) {
331
+ const registry = this.deps.scope.skills;
332
+ if (!registry) return void 0;
333
+ return registry.findByName(id) ?? (id.includes(":") ? registry.findByQualifiedName(id) : void 0) ?? registry.getSkills(true).find((skill) => (skill.metadata.id ?? skill.name) === id || skill.metadata.name === id);
334
+ }
335
+ };
336
+
183
337
  // plugins/plugin-skilled-openapi/src/skilled-openapi.symbols.ts
184
338
  var SkilledOpenApiConfig = class {
185
339
  constructor(options) {
@@ -642,6 +796,7 @@ function collectPathsWithGet(entries) {
642
796
  }
643
797
 
644
798
  // plugins/plugin-skilled-openapi/src/tools/load-skill.tool.ts
799
+ import { BundleStore } from "@frontmcp/adapters/skills";
645
800
  import { InternalMcpError, PublicMcpError, ScopeEntry, Tool, ToolContext } from "@frontmcp/sdk";
646
801
 
647
802
  // plugins/plugin-skilled-openapi/src/tools/load-skill.schema.ts
@@ -690,11 +845,25 @@ var LoadSkillTool = class extends ToolContext {
690
845
  if (!skillRegistry) {
691
846
  throw new InternalMcpError("SkillRegistry is not available on the active scope", "SKILL_REGISTRY_UNAVAILABLE");
692
847
  }
848
+ const notFound = () => (
849
+ // Caller-visible: skill id was not registered, or the caller may not see it
850
+ // (the same answer, so a refused skill's existence isn't revealed). Map to a
851
+ // 404 with a stable code so MCP clients can branch on it.
852
+ new PublicMcpError(`Skill "${input.skillId}" not found`, "SKILL_NOT_FOUND", 404)
853
+ );
693
854
  const result = await skillRegistry.loadSkill(input.skillId);
694
- if (!result) {
695
- throw new PublicMcpError(`Skill "${input.skillId}" not found`, "SKILL_NOT_FOUND", 404);
696
- }
855
+ if (!result) throw notFound();
856
+ const config = this.get(SkilledOpenApiConfig);
857
+ const visibility = new SkillVisibility({
858
+ scope,
859
+ guard: this.get(AuthorityGuard),
860
+ bundle: this.get(BundleStore).current(),
861
+ unprotectedOps: config.unprotectedOps,
862
+ authInfo: this.authInfo
863
+ });
697
864
  const skill = result.skill;
865
+ if (!await visibility.isVisible(input.skillId) || !await visibility.isVisible(skill.id)) throw notFound();
866
+ const actions = skill.actions ? await visibility.visibleActions(skill.id, skill.actions, this.get(HiddenOpRegistry)) : void 0;
698
867
  return {
699
868
  skill: {
700
869
  id: skill.id,
@@ -702,7 +871,7 @@ var LoadSkillTool = class extends ToolContext {
702
871
  description: skill.description,
703
872
  instructions: skill.instructions,
704
873
  ...skill.bundleVersion !== void 0 && { bundleVersion: skill.bundleVersion },
705
- ...skill.actions ? { actions: skill.actions } : {}
874
+ ...actions ? { actions } : {}
706
875
  },
707
876
  isComplete: result.isComplete,
708
877
  ...result.warning !== void 0 && { warning: result.warning }
@@ -1539,6 +1708,7 @@ var searchSkillOutputSchema = {
1539
1708
  };
1540
1709
 
1541
1710
  // plugins/plugin-skilled-openapi/src/tools/search-skill.tool.ts
1711
+ import { BundleStore as BundleStore2 } from "@frontmcp/adapters/skills";
1542
1712
  import { ScopeEntry as ScopeEntry2, Tool as Tool3, ToolContext as ToolContext3 } from "@frontmcp/sdk";
1543
1713
  var SearchSkillTool = class extends ToolContext3 {
1544
1714
  async execute(input) {
@@ -1550,14 +1720,23 @@ var SearchSkillTool = class extends ToolContext3 {
1550
1720
  }
1551
1721
  const limit = input.limit ?? 20;
1552
1722
  const tags = input.tags;
1553
- const results = await skillRegistry.search(input.query, {
1554
- topK: limit,
1723
+ const found = await skillRegistry.search(input.query, {
1724
+ // Over-fetch: skills the caller may not see are removed below.
1725
+ topK: limit * 4,
1555
1726
  ...tags ? { tags } : {},
1556
1727
  // Anti-query demotion (honored when the skill index supports it) — surfaces
1557
1728
  // what the user wants while pushing down what they explicitly do not.
1558
1729
  ...input.notQuery !== void 0 ? { negativeQuery: input.notQuery } : {},
1559
1730
  ...input.notWeight !== void 0 ? { negativeWeight: input.notWeight } : {}
1560
1731
  });
1732
+ const visibility = new SkillVisibility({
1733
+ scope,
1734
+ guard: this.get(AuthorityGuard),
1735
+ bundle: this.get(BundleStore2).current(),
1736
+ unprotectedOps: this.get(SkilledOpenApiConfig).unprotectedOps,
1737
+ authInfo: this.authInfo
1738
+ });
1739
+ const results = (await visibility.filterVisible(found, (r) => r.metadata.id ?? r.metadata.name)).slice(0, limit);
1561
1740
  return {
1562
1741
  skills: results.map((r) => ({
1563
1742
  skillId: r.metadata.id ?? r.metadata.name,
@@ -1584,6 +1763,7 @@ SearchSkillTool = __decorateClass([
1584
1763
 
1585
1764
  // plugins/plugin-skilled-openapi/src/skilled-openapi.plugin.ts
1586
1765
  var TELEMETRY_FACTORY_TOKEN = /* @__PURE__ */ Symbol.for("frontmcp:observability:telemetry-factory");
1766
+ var FilterSkillsHook = FlowHooksOf("skills:filter");
1587
1767
  var SKILLED_OPENAPI_RUNTIME_DEPS_TOKEN = /* @__PURE__ */ Symbol.for("frontmcp:skilled-openapi:runtime-deps");
1588
1768
  function resolveRuntimeDeps(scope) {
1589
1769
  const providers = scope?.providers;
@@ -1634,15 +1814,61 @@ var SkilledOpenApiPlugin = class extends DynamicPlugin {
1634
1814
  } catch {
1635
1815
  }
1636
1816
  const scope = this.get(ScopeEntry3);
1817
+ const visible = await this.skillVisibility(scope, flowCtx.state.authInfo).filterVisibleEntries(
1818
+ scope.skills?.getSkills({ visibility: "mcp" }) ?? []
1819
+ );
1637
1820
  const catalog = buildSkillsCatalogSummary(scope.skills, {
1638
- mcpResources: scope.metadata?.skillsConfig?.mcpResources
1821
+ mcpResources: scope.metadata?.skillsConfig?.mcpResources,
1822
+ skills: visible
1639
1823
  });
1640
1824
  const description = catalog ? `${searchSkillDescription}
1641
1825
 
1642
1826
  ---
1643
1827
 
1644
1828
  ${catalog}` : searchSkillDescription;
1645
- target.tool.metadata.description = description;
1829
+ const perRequestTool = Object.create(target.tool, {
1830
+ metadata: { value: { ...target.tool.metadata, description }, enumerable: true }
1831
+ });
1832
+ flowCtx.state.set(
1833
+ "tools",
1834
+ tools.map((item) => item === target ? { ...item, tool: perRequestTool } : item)
1835
+ );
1836
+ }
1837
+ async hideBundleSkillsFromCaller(flowCtx) {
1838
+ const { skills } = flowCtx.state;
1839
+ if (!skills || skills.length === 0) return;
1840
+ const bundle = this.get(BundleStore3).current();
1841
+ const policies = bundleSkillPolicies(bundle);
1842
+ if (policies.size === 0) return;
1843
+ const visibility = new SkillVisibility({
1844
+ scope: this.get(ScopeEntry3),
1845
+ guard: this.get(AuthorityGuard),
1846
+ bundle,
1847
+ unprotectedOps: this.options.unprotectedOps,
1848
+ authInfo: this.currentAuthInfo()
1849
+ });
1850
+ const kept = [];
1851
+ for (const skill of skills) {
1852
+ if (await visibility.passesBundleSkillRule(skill.metadata.id ?? skill.name)) kept.push(skill);
1853
+ }
1854
+ if (kept.length !== skills.length) flowCtx.state.set("skills", kept);
1855
+ }
1856
+ skillVisibility(scope, authInfo) {
1857
+ return new SkillVisibility({
1858
+ scope,
1859
+ guard: this.get(AuthorityGuard),
1860
+ bundle: this.get(BundleStore3).current(),
1861
+ unprotectedOps: this.options.unprotectedOps,
1862
+ authInfo
1863
+ });
1864
+ }
1865
+ /** The AuthInfo of the request being served; an anonymous caller when there is none. */
1866
+ currentAuthInfo() {
1867
+ try {
1868
+ return this.get(FrontMcpContextStorage)?.getStore()?.authInfo ?? {};
1869
+ } catch {
1870
+ return {};
1871
+ }
1646
1872
  }
1647
1873
  warnIfInsecureConfig() {
1648
1874
  if (this.options.dev) {
@@ -1664,7 +1890,7 @@ ${catalog}` : searchSkillDescription;
1664
1890
  { name: "skilled-openapi:hidden-ops", provide: HiddenOpRegistry, useValue: new HiddenOpRegistry() },
1665
1891
  {
1666
1892
  name: "skilled-openapi:bundle-store",
1667
- provide: BundleStore,
1893
+ provide: BundleStore3,
1668
1894
  // Resolve TelemetryAccessor from the scope's provider registry so the
1669
1895
  // bundle-pulls counter and `skill.bundle.swap` span actually export.
1670
1896
  // The accessor is structurally compatible with `BundleStoreTelemetry`.
@@ -1672,7 +1898,7 @@ ${catalog}` : searchSkillDescription;
1672
1898
  // we resolve `undefined` and the BundleStore falls through its zero-cost
1673
1899
  // no-telemetry path.
1674
1900
  inject: () => [ScopeEntry3],
1675
- useFactory: (scope) => new BundleStore({ telemetry: resolveBundleTelemetry(scope) })
1901
+ useFactory: (scope) => new BundleStore3({ telemetry: resolveBundleTelemetry(scope) })
1676
1902
  },
1677
1903
  {
1678
1904
  name: "skilled-openapi:credential-resolver",
@@ -1683,12 +1909,22 @@ ${catalog}` : searchSkillDescription;
1683
1909
  name: "skilled-openapi:authority-guard",
1684
1910
  provide: AuthorityGuard,
1685
1911
  inject: () => [ScopeEntry3],
1686
- useFactory: (scope) => new AuthorityGuard({ logger: scope.logger.child("skilled-openapi:authority") })
1912
+ useFactory: (scope) => new AuthorityGuard({
1913
+ logger: scope.logger.child("skilled-openapi:authority"),
1914
+ // Bundle rules follow the server's authorities settings (claimsMapping,
1915
+ // claimsResolver, relationshipResolver, evaluators) when it has them. Read per check:
1916
+ // the server may register its engine after this provider is built.
1917
+ serverAuthorities: () => {
1918
+ const engine = scope.authoritiesEngine;
1919
+ const contextBuilder = scope.authoritiesContextBuilder;
1920
+ return engine && contextBuilder ? { engine, contextBuilder } : void 0;
1921
+ }
1922
+ })
1687
1923
  },
1688
1924
  {
1689
1925
  name: "skilled-openapi:bundle-sync",
1690
1926
  provide: BundleSyncService,
1691
- inject: () => [ScopeEntry3, HiddenOpRegistry, BundleStore],
1927
+ inject: () => [ScopeEntry3, HiddenOpRegistry, BundleStore3],
1692
1928
  useFactory: async (scope, hiddenOps, bundleStore) => {
1693
1929
  const logger = scope.logger.child("skilled-openapi:sync");
1694
1930
  const lazySkillRegistry = new Proxy({}, {
@@ -1773,6 +2009,9 @@ ${catalog}` : searchSkillDescription;
1773
2009
  __decorateClass([
1774
2010
  ListToolsHook.Did("findTools", { priority: 50 })
1775
2011
  ], SkilledOpenApiPlugin.prototype, "injectSkillCatalogIntoSearchTool", 1);
2012
+ __decorateClass([
2013
+ FilterSkillsHook.Did("filterSkills", { priority: 50 })
2014
+ ], SkilledOpenApiPlugin.prototype, "hideBundleSkillsFromCaller", 1);
1776
2015
  SkilledOpenApiPlugin = __decorateClass([
1777
2016
  Plugin({
1778
2017
  name: "skilled-openapi",
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-skilled-openapi",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "FrontMCP plugin: serve a customer's OpenAPI spec as skill bundles (signed, OpenAPI-Overlay-based) with hidden per-operation tools mediated by three meta-tools (search_skill / load_skill / run_workflow — the last runs an enclave-sandboxed AgentScript that orchestrates the loaded skills' operations).",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -48,15 +48,15 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@frontmcp/adapters": "1.8.2",
52
- "@frontmcp/auth": "1.8.2",
53
- "@frontmcp/sdk": "1.8.2",
54
- "@frontmcp/utils": "1.8.2"
51
+ "@frontmcp/adapters": "1.8.3",
52
+ "@frontmcp/auth": "1.8.3",
53
+ "@frontmcp/sdk": "1.8.3",
54
+ "@frontmcp/utils": "1.8.3"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@enclave-vm/ast": "*",
58
58
  "@enclave-vm/core": "*",
59
- "@frontmcp/lazy-zod": "1.8.2"
59
+ "@frontmcp/lazy-zod": "1.8.3"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "@enclave-vm/ast": {
package/index.js CHANGED
@@ -48,8 +48,8 @@ __export(index_exports, {
48
48
  module.exports = __toCommonJS(index_exports);
49
49
 
50
50
  // plugins/plugin-skilled-openapi/src/skilled-openapi.plugin.ts
51
- var import_skills6 = require("@frontmcp/adapters/skills");
52
- var import_sdk5 = require("@frontmcp/sdk");
51
+ var import_skills8 = require("@frontmcp/adapters/skills");
52
+ var import_sdk6 = require("@frontmcp/sdk");
53
53
 
54
54
  // plugins/plugin-skilled-openapi/src/executor/credential-resolver.ts
55
55
  var MemoryCredentialResolver = class {
@@ -145,16 +145,61 @@ var HiddenOpRegistry = class {
145
145
 
146
146
  // plugins/plugin-skilled-openapi/src/security/authority-guard.ts
147
147
  var import_auth = require("@frontmcp/auth");
148
+ function policyDependsOnInput(value) {
149
+ if (typeof value === "string") return value === "input" || value.startsWith("input.");
150
+ if (Array.isArray(value)) return value.some(policyDependsOnInput);
151
+ if (value !== null && typeof value === "object") {
152
+ for (const [key2, nested] of Object.entries(value)) {
153
+ if (key2 === "fromInput" || key2 === "custom" || key2 === "guards") return true;
154
+ if (policyDependsOnInput(key2) || policyDependsOnInput(nested)) return true;
155
+ }
156
+ }
157
+ return false;
158
+ }
159
+ function judgeablePart(policy, engine) {
160
+ if (policy === void 0 || policy === null) return void 0;
161
+ const profileDependsOnInput = (name) => typeof name === "string" && policyDependsOnInput(engine.resolveProfile(name));
162
+ const value = policy;
163
+ if (typeof value === "string") return profileDependsOnInput(value) ? void 0 : policy;
164
+ if (Array.isArray(value)) {
165
+ const names = value.filter((name) => !profileDependsOnInput(name));
166
+ return names.length > 0 ? names : void 0;
167
+ }
168
+ return policyDependsOnInput(policy) ? void 0 : policy;
169
+ }
148
170
  var AuthorityGuard = class {
149
171
  engine;
150
172
  contextBuilder;
151
173
  logger;
174
+ serverAuthorities;
152
175
  constructor(opts = {}) {
153
176
  const profiles = opts.profiles ?? new import_auth.AuthoritiesProfileRegistry();
154
177
  const evaluators = opts.evaluators ?? new import_auth.AuthoritiesEvaluatorRegistry();
155
178
  this.engine = new import_auth.AuthoritiesEngine(profiles, evaluators);
156
179
  this.contextBuilder = new import_auth.AuthoritiesContextBuilder();
157
180
  this.logger = opts.logger;
181
+ this.serverAuthorities = opts.serverAuthorities;
182
+ }
183
+ /** The server's engine and context builder when it configures authorities, else the guard's own. */
184
+ authorities() {
185
+ return this.serverAuthorities?.() ?? { engine: this.engine, contextBuilder: this.contextBuilder };
186
+ }
187
+ /**
188
+ * Whether the caller may be shown a skill or action (in `search_skill`,
189
+ * `load_skill`, the catalog and the SDK skill surfaces), judged without the
190
+ * action's input. A rule that depends on the input can't be judged yet and
191
+ * doesn't hide anything; the call is still checked by {@link check}. Every other
192
+ * rule must grant, and a policy-less action under `unprotectedOps: 'deny'` must
193
+ * be public, exactly as at call time.
194
+ */
195
+ async canDiscover(args) {
196
+ const { engine } = this.authorities();
197
+ const hasPolicy = [args.skillPolicy, args.policy].some((p) => p !== void 0 && p !== null);
198
+ const skillPolicy = judgeablePart(args.skillPolicy, engine);
199
+ const policy = judgeablePart(args.policy, engine);
200
+ if (hasPolicy && skillPolicy === void 0 && policy === void 0) return true;
201
+ const result = await this.check({ ...args, skillPolicy, policy, input: {} });
202
+ return result.granted;
158
203
  }
159
204
  async check(args) {
160
205
  const { policy, skillPolicy, isPublic, authInfo, input, env } = args;
@@ -172,10 +217,11 @@ var AuthorityGuard = class {
172
217
  return { granted: true, evaluatedPolicies: [] };
173
218
  }
174
219
  try {
175
- const ctx = this.contextBuilder.build(authInfo, input, env);
220
+ const { engine, contextBuilder } = this.authorities();
221
+ const ctx = contextBuilder.build(authInfo, input, env);
176
222
  const evaluatedPolicies = [];
177
223
  for (const p of policies) {
178
- const res = await this.engine.evaluate(p, ctx);
224
+ const res = await engine.evaluate(p, ctx);
179
225
  evaluatedPolicies.push(...res.evaluatedPolicies ?? []);
180
226
  if (!res.granted) {
181
227
  return { ...res, evaluatedPolicies };
@@ -203,6 +249,112 @@ function normalizeCaughtMessage(e) {
203
249
  return String(e ?? "authority evaluation threw");
204
250
  }
205
251
 
252
+ // plugins/plugin-skilled-openapi/src/security/skill-visibility.ts
253
+ var import_sdk = require("@frontmcp/sdk");
254
+ function bundleSkillPolicies(bundle) {
255
+ const policies = /* @__PURE__ */ new Map();
256
+ for (const skill of bundle?.skills ?? []) {
257
+ if (skill.requiredAuthorities !== void 0 && skill.requiredAuthorities !== null) {
258
+ policies.set(skill.id, skill.requiredAuthorities);
259
+ }
260
+ }
261
+ return policies;
262
+ }
263
+ var SkillVisibility = class {
264
+ constructor(deps) {
265
+ this.deps = deps;
266
+ this.policies = bundleSkillPolicies(deps.bundle);
267
+ this.authInfo = deps.authInfo ?? {};
268
+ }
269
+ deps;
270
+ policies;
271
+ authInfo;
272
+ /** Whether the caller satisfies the bundle skill's own `requiredAuthorities` (true for other skills). */
273
+ async passesBundleSkillRule(skillId) {
274
+ const skillPolicy = this.policies.get(skillId);
275
+ if (skillPolicy === void 0) return true;
276
+ return this.deps.guard.canDiscover({ skillPolicy, authInfo: this.authInfo });
277
+ }
278
+ /**
279
+ * Keep the items whose skill the caller may see: the bundle skill's rule, the
280
+ * `skills:filter` flow and the server's `@Skill({ authorities })` rule. An item
281
+ * whose id names no registered skill (an external skill) is judged by the bundle
282
+ * rule alone, as the SDK's own surfaces keep such results.
283
+ */
284
+ async filterVisible(items, idOf) {
285
+ if (items.length === 0) return [];
286
+ const entries = items.map((item) => this.resolveEntry(idOf(item)));
287
+ const registered = [...new Set(entries.filter((entry) => entry !== void 0))];
288
+ const visibleEntries = new Set(await this.filterVisibleEntries(registered));
289
+ const visible = [];
290
+ for (const [index, item] of items.entries()) {
291
+ const entry = entries[index];
292
+ if (entry ? !visibleEntries.has(entry) : !await this.passesBundleSkillRule(idOf(item))) continue;
293
+ visible.push(item);
294
+ }
295
+ return visible;
296
+ }
297
+ /** The registered skills the caller may see, in their order (see {@link filterVisible}). */
298
+ async filterVisibleEntries(entries) {
299
+ if (entries.length === 0) return [];
300
+ const servable = new Set(await (0, import_sdk.filterServableSkills)(this.deps.scope, entries));
301
+ const visible = [];
302
+ for (const entry of entries) {
303
+ if (!servable.has(entry) || !await this.passesSkillAuthorities(entry)) continue;
304
+ if (!await this.passesBundleSkillRule(entry.metadata.id ?? entry.name)) continue;
305
+ visible.push(entry);
306
+ }
307
+ return visible;
308
+ }
309
+ /** Whether the caller may see the skill named by `skillId` (see {@link filterVisible}). */
310
+ async isVisible(skillId) {
311
+ return (await this.filterVisible([skillId], (id) => id)).length === 1;
312
+ }
313
+ /**
314
+ * The actions of a bundle skill the caller may be shown: an action whose rules
315
+ * refuse the caller whatever the input (or that `unprotectedOps: 'deny'` blocks)
316
+ * is left out. Rules that depend on the input keep the action listed; the call is
317
+ * checked again when it runs.
318
+ */
319
+ async visibleActions(skillId, actions, hiddenOps) {
320
+ const visible = [];
321
+ for (const action of actions) {
322
+ const entry = hiddenOps.get(skillId, action.actionId);
323
+ if (!entry) continue;
324
+ const allowed = await this.deps.guard.canDiscover({
325
+ policy: entry.op.requiredAuthorities,
326
+ skillPolicy: entry.skillRequiredAuthorities,
327
+ isPublic: entry.op.public,
328
+ unprotectedOps: this.deps.unprotectedOps,
329
+ authInfo: this.authInfo
330
+ });
331
+ if (allowed) visible.push(action);
332
+ }
333
+ return visible;
334
+ }
335
+ /** The server's `@Skill({ authorities })` rule, evaluated as the SDK's skill surfaces do. */
336
+ async passesSkillAuthorities(entry) {
337
+ const { authoritiesEngine: engine, authoritiesContextBuilder: contextBuilder } = this.deps.scope;
338
+ const authorities = entry.metadata["authorities"];
339
+ if (!engine || !contextBuilder || !authorities) return true;
340
+ try {
341
+ const result = await engine.evaluate(
342
+ authorities,
343
+ contextBuilder.build(this.authInfo)
344
+ );
345
+ return result.granted;
346
+ } catch {
347
+ return false;
348
+ }
349
+ }
350
+ /** The registered skill an id names, looked up the way the SDK's skill surfaces do. */
351
+ resolveEntry(id) {
352
+ const registry = this.deps.scope.skills;
353
+ if (!registry) return void 0;
354
+ return registry.findByName(id) ?? (id.includes(":") ? registry.findByQualifiedName(id) : void 0) ?? registry.getSkills(true).find((skill) => (skill.metadata.id ?? skill.name) === id || skill.metadata.name === id);
355
+ }
356
+ };
357
+
206
358
  // plugins/plugin-skilled-openapi/src/skilled-openapi.symbols.ts
207
359
  var SkilledOpenApiConfig = class {
208
360
  constructor(options) {
@@ -651,7 +803,8 @@ function collectPathsWithGet(entries) {
651
803
  }
652
804
 
653
805
  // plugins/plugin-skilled-openapi/src/tools/load-skill.tool.ts
654
- var import_sdk = require("@frontmcp/sdk");
806
+ var import_skills4 = require("@frontmcp/adapters/skills");
807
+ var import_sdk2 = require("@frontmcp/sdk");
655
808
 
656
809
  // plugins/plugin-skilled-openapi/src/tools/load-skill.schema.ts
657
810
  var import_lazy_zod2 = require("@frontmcp/lazy-zod");
@@ -691,19 +844,33 @@ var loadSkillOutputSchema = {
691
844
  };
692
845
 
693
846
  // plugins/plugin-skilled-openapi/src/tools/load-skill.tool.ts
694
- var LoadSkillTool = class extends import_sdk.ToolContext {
847
+ var LoadSkillTool = class extends import_sdk2.ToolContext {
695
848
  async execute(input) {
696
849
  await this.get(BundleSyncService).ensureReady();
697
- const scope = this.get(import_sdk.ScopeEntry);
850
+ const scope = this.get(import_sdk2.ScopeEntry);
698
851
  const skillRegistry = scope.skills;
699
852
  if (!skillRegistry) {
700
- throw new import_sdk.InternalMcpError("SkillRegistry is not available on the active scope", "SKILL_REGISTRY_UNAVAILABLE");
853
+ throw new import_sdk2.InternalMcpError("SkillRegistry is not available on the active scope", "SKILL_REGISTRY_UNAVAILABLE");
701
854
  }
855
+ const notFound = () => (
856
+ // Caller-visible: skill id was not registered, or the caller may not see it
857
+ // (the same answer, so a refused skill's existence isn't revealed). Map to a
858
+ // 404 with a stable code so MCP clients can branch on it.
859
+ new import_sdk2.PublicMcpError(`Skill "${input.skillId}" not found`, "SKILL_NOT_FOUND", 404)
860
+ );
702
861
  const result = await skillRegistry.loadSkill(input.skillId);
703
- if (!result) {
704
- throw new import_sdk.PublicMcpError(`Skill "${input.skillId}" not found`, "SKILL_NOT_FOUND", 404);
705
- }
862
+ if (!result) throw notFound();
863
+ const config = this.get(SkilledOpenApiConfig);
864
+ const visibility = new SkillVisibility({
865
+ scope,
866
+ guard: this.get(AuthorityGuard),
867
+ bundle: this.get(import_skills4.BundleStore).current(),
868
+ unprotectedOps: config.unprotectedOps,
869
+ authInfo: this.authInfo
870
+ });
706
871
  const skill = result.skill;
872
+ if (!await visibility.isVisible(input.skillId) || !await visibility.isVisible(skill.id)) throw notFound();
873
+ const actions = skill.actions ? await visibility.visibleActions(skill.id, skill.actions, this.get(HiddenOpRegistry)) : void 0;
707
874
  return {
708
875
  skill: {
709
876
  id: skill.id,
@@ -711,7 +878,7 @@ var LoadSkillTool = class extends import_sdk.ToolContext {
711
878
  description: skill.description,
712
879
  instructions: skill.instructions,
713
880
  ...skill.bundleVersion !== void 0 && { bundleVersion: skill.bundleVersion },
714
- ...skill.actions ? { actions: skill.actions } : {}
881
+ ...actions ? { actions } : {}
715
882
  },
716
883
  isComplete: result.isComplete,
717
884
  ...result.warning !== void 0 && { warning: result.warning }
@@ -719,7 +886,7 @@ var LoadSkillTool = class extends import_sdk.ToolContext {
719
886
  }
720
887
  };
721
888
  LoadSkillTool = __decorateClass([
722
- (0, import_sdk.Tool)({
889
+ (0, import_sdk2.Tool)({
723
890
  name: "load_skill",
724
891
  description: loadSkillDescription,
725
892
  inputSchema: loadSkillInputSchema,
@@ -732,8 +899,8 @@ LoadSkillTool = __decorateClass([
732
899
  ], LoadSkillTool);
733
900
 
734
901
  // plugins/plugin-skilled-openapi/src/tools/operation-tool.factory.ts
735
- var import_skills4 = require("@frontmcp/adapters/skills");
736
- var import_sdk2 = require("@frontmcp/sdk");
902
+ var import_skills5 = require("@frontmcp/adapters/skills");
903
+ var import_sdk3 = require("@frontmcp/sdk");
737
904
 
738
905
  // plugins/plugin-skilled-openapi/src/executor/openapi-runtime.ts
739
906
  var import_openapi = require("@frontmcp/adapters/openapi");
@@ -1147,16 +1314,16 @@ var OperationToolFactory = class {
1147
1314
  const executor = this.makeExecutor(entry);
1148
1315
  const metadata = buildOperationMetadata(entry);
1149
1316
  const record = {
1150
- kind: import_sdk2.ToolKind.FUNCTION,
1317
+ kind: import_sdk3.ToolKind.FUNCTION,
1151
1318
  provide: executor,
1152
1319
  metadata
1153
1320
  };
1154
- const instance = new import_sdk2.ToolInstance(record, this.deps.providers, OPERATION_TOOL_OWNER);
1321
+ const instance = new import_sdk3.ToolInstance(record, this.deps.providers, OPERATION_TOOL_OWNER);
1155
1322
  this.deps.toolRegistry.registerToolInstance(instance);
1156
1323
  this.registered.set(key2, executor);
1157
1324
  if (this.deps.classificationRegistry && ctx?.pathsWithGet) {
1158
1325
  try {
1159
- const classification = (0, import_skills4.classifyOne)(
1326
+ const classification = (0, import_skills5.classifyOne)(
1160
1327
  entry.bundleId,
1161
1328
  {
1162
1329
  operationId: entry.op.operationId,
@@ -1270,8 +1437,8 @@ var OperationToolFactory = class {
1270
1437
  };
1271
1438
 
1272
1439
  // plugins/plugin-skilled-openapi/src/tools/run-workflow.tool.ts
1273
- var import_skills5 = require("@frontmcp/adapters/skills");
1274
- var import_sdk3 = require("@frontmcp/sdk");
1440
+ var import_skills6 = require("@frontmcp/adapters/skills");
1441
+ var import_sdk4 = require("@frontmcp/sdk");
1275
1442
 
1276
1443
  // plugins/plugin-skilled-openapi/src/executor/execute-skill-action.ts
1277
1444
  function formatZodIssues(issues) {
@@ -1419,7 +1586,7 @@ var runWorkflowOutputSchema = {
1419
1586
  var MAX_STEPS = 2e6;
1420
1587
  var MAX_TOOL_CALLS = 25;
1421
1588
  var TIMEOUT_MS = 8e3;
1422
- var RunWorkflowTool = class extends import_sdk3.ToolContext {
1589
+ var RunWorkflowTool = class extends import_sdk4.ToolContext {
1423
1590
  async execute(input) {
1424
1591
  await this.get(BundleSyncService).ensureReady();
1425
1592
  const config = this.get(SkilledOpenApiConfig);
@@ -1437,7 +1604,7 @@ var RunWorkflowTool = class extends import_sdk3.ToolContext {
1437
1604
  error: "workflow execution unavailable: the @enclave-vm sandbox is not installed. Install @enclave-vm/core and @enclave-vm/ast to enable run_workflow."
1438
1605
  };
1439
1606
  }
1440
- const auditWriter = this.tryGet(import_skills5.SkillAuditWriterToken);
1607
+ const auditWriter = this.tryGet(import_skills6.SkillAuditWriterToken);
1441
1608
  const deps = {
1442
1609
  config: { outbound: config.outbound, unprotectedOps: config.unprotectedOps },
1443
1610
  resolver: { resolve: (ref, opts) => resolver.resolve(ref, opts) },
@@ -1488,7 +1655,7 @@ var RunWorkflowTool = class extends import_sdk3.ToolContext {
1488
1655
  }
1489
1656
  };
1490
1657
  RunWorkflowTool = __decorateClass([
1491
- (0, import_sdk3.Tool)({
1658
+ (0, import_sdk4.Tool)({
1492
1659
  name: "run_workflow",
1493
1660
  description: runWorkflowDescription,
1494
1661
  inputSchema: runWorkflowInputSchema,
@@ -1542,25 +1709,35 @@ var searchSkillOutputSchema = {
1542
1709
  };
1543
1710
 
1544
1711
  // plugins/plugin-skilled-openapi/src/tools/search-skill.tool.ts
1545
- var import_sdk4 = require("@frontmcp/sdk");
1546
- var SearchSkillTool = class extends import_sdk4.ToolContext {
1712
+ var import_skills7 = require("@frontmcp/adapters/skills");
1713
+ var import_sdk5 = require("@frontmcp/sdk");
1714
+ var SearchSkillTool = class extends import_sdk5.ToolContext {
1547
1715
  async execute(input) {
1548
1716
  await this.get(BundleSyncService).ensureReady();
1549
- const scope = this.get(import_sdk4.ScopeEntry);
1717
+ const scope = this.get(import_sdk5.ScopeEntry);
1550
1718
  const skillRegistry = scope.skills;
1551
1719
  if (!skillRegistry || !skillRegistry.hasAny()) {
1552
1720
  return { skills: [] };
1553
1721
  }
1554
1722
  const limit = input.limit ?? 20;
1555
1723
  const tags = input.tags;
1556
- const results = await skillRegistry.search(input.query, {
1557
- topK: limit,
1724
+ const found = await skillRegistry.search(input.query, {
1725
+ // Over-fetch: skills the caller may not see are removed below.
1726
+ topK: limit * 4,
1558
1727
  ...tags ? { tags } : {},
1559
1728
  // Anti-query demotion (honored when the skill index supports it) — surfaces
1560
1729
  // what the user wants while pushing down what they explicitly do not.
1561
1730
  ...input.notQuery !== void 0 ? { negativeQuery: input.notQuery } : {},
1562
1731
  ...input.notWeight !== void 0 ? { negativeWeight: input.notWeight } : {}
1563
1732
  });
1733
+ const visibility = new SkillVisibility({
1734
+ scope,
1735
+ guard: this.get(AuthorityGuard),
1736
+ bundle: this.get(import_skills7.BundleStore).current(),
1737
+ unprotectedOps: this.get(SkilledOpenApiConfig).unprotectedOps,
1738
+ authInfo: this.authInfo
1739
+ });
1740
+ const results = (await visibility.filterVisible(found, (r) => r.metadata.id ?? r.metadata.name)).slice(0, limit);
1564
1741
  return {
1565
1742
  skills: results.map((r) => ({
1566
1743
  skillId: r.metadata.id ?? r.metadata.name,
@@ -1573,7 +1750,7 @@ var SearchSkillTool = class extends import_sdk4.ToolContext {
1573
1750
  }
1574
1751
  };
1575
1752
  SearchSkillTool = __decorateClass([
1576
- (0, import_sdk4.Tool)({
1753
+ (0, import_sdk5.Tool)({
1577
1754
  name: "search_skill",
1578
1755
  description: searchSkillDescription,
1579
1756
  inputSchema: searchSkillInputSchema,
@@ -1587,6 +1764,7 @@ SearchSkillTool = __decorateClass([
1587
1764
 
1588
1765
  // plugins/plugin-skilled-openapi/src/skilled-openapi.plugin.ts
1589
1766
  var TELEMETRY_FACTORY_TOKEN = /* @__PURE__ */ Symbol.for("frontmcp:observability:telemetry-factory");
1767
+ var FilterSkillsHook = (0, import_sdk6.FlowHooksOf)("skills:filter");
1590
1768
  var SKILLED_OPENAPI_RUNTIME_DEPS_TOKEN = /* @__PURE__ */ Symbol.for("frontmcp:skilled-openapi:runtime-deps");
1591
1769
  function resolveRuntimeDeps(scope) {
1592
1770
  const providers = scope?.providers;
@@ -1613,7 +1791,7 @@ function resolveBundleTelemetry(scope) {
1613
1791
  startSpan: (name, attributes) => factory.startSpan(name, attributes)
1614
1792
  };
1615
1793
  }
1616
- var SkilledOpenApiPlugin = class extends import_sdk5.DynamicPlugin {
1794
+ var SkilledOpenApiPlugin = class extends import_sdk6.DynamicPlugin {
1617
1795
  options;
1618
1796
  cachedLogger;
1619
1797
  constructor(options) {
@@ -1623,7 +1801,7 @@ var SkilledOpenApiPlugin = class extends import_sdk5.DynamicPlugin {
1623
1801
  }
1624
1802
  getLogger() {
1625
1803
  if (!this.cachedLogger) {
1626
- this.cachedLogger = this.get(import_sdk5.FrontMcpLogger).child("skilled-openapi");
1804
+ this.cachedLogger = this.get(import_sdk6.FrontMcpLogger).child("skilled-openapi");
1627
1805
  }
1628
1806
  return this.cachedLogger;
1629
1807
  }
@@ -1636,16 +1814,62 @@ var SkilledOpenApiPlugin = class extends import_sdk5.DynamicPlugin {
1636
1814
  await this.get(BundleSyncService).ensureReady();
1637
1815
  } catch {
1638
1816
  }
1639
- const scope = this.get(import_sdk5.ScopeEntry);
1640
- const catalog = (0, import_sdk5.buildSkillsCatalogSummary)(scope.skills, {
1641
- mcpResources: scope.metadata?.skillsConfig?.mcpResources
1817
+ const scope = this.get(import_sdk6.ScopeEntry);
1818
+ const visible = await this.skillVisibility(scope, flowCtx.state.authInfo).filterVisibleEntries(
1819
+ scope.skills?.getSkills({ visibility: "mcp" }) ?? []
1820
+ );
1821
+ const catalog = (0, import_sdk6.buildSkillsCatalogSummary)(scope.skills, {
1822
+ mcpResources: scope.metadata?.skillsConfig?.mcpResources,
1823
+ skills: visible
1642
1824
  });
1643
1825
  const description = catalog ? `${searchSkillDescription}
1644
1826
 
1645
1827
  ---
1646
1828
 
1647
1829
  ${catalog}` : searchSkillDescription;
1648
- target.tool.metadata.description = description;
1830
+ const perRequestTool = Object.create(target.tool, {
1831
+ metadata: { value: { ...target.tool.metadata, description }, enumerable: true }
1832
+ });
1833
+ flowCtx.state.set(
1834
+ "tools",
1835
+ tools.map((item) => item === target ? { ...item, tool: perRequestTool } : item)
1836
+ );
1837
+ }
1838
+ async hideBundleSkillsFromCaller(flowCtx) {
1839
+ const { skills } = flowCtx.state;
1840
+ if (!skills || skills.length === 0) return;
1841
+ const bundle = this.get(import_skills8.BundleStore).current();
1842
+ const policies = bundleSkillPolicies(bundle);
1843
+ if (policies.size === 0) return;
1844
+ const visibility = new SkillVisibility({
1845
+ scope: this.get(import_sdk6.ScopeEntry),
1846
+ guard: this.get(AuthorityGuard),
1847
+ bundle,
1848
+ unprotectedOps: this.options.unprotectedOps,
1849
+ authInfo: this.currentAuthInfo()
1850
+ });
1851
+ const kept = [];
1852
+ for (const skill of skills) {
1853
+ if (await visibility.passesBundleSkillRule(skill.metadata.id ?? skill.name)) kept.push(skill);
1854
+ }
1855
+ if (kept.length !== skills.length) flowCtx.state.set("skills", kept);
1856
+ }
1857
+ skillVisibility(scope, authInfo) {
1858
+ return new SkillVisibility({
1859
+ scope,
1860
+ guard: this.get(AuthorityGuard),
1861
+ bundle: this.get(import_skills8.BundleStore).current(),
1862
+ unprotectedOps: this.options.unprotectedOps,
1863
+ authInfo
1864
+ });
1865
+ }
1866
+ /** The AuthInfo of the request being served; an anonymous caller when there is none. */
1867
+ currentAuthInfo() {
1868
+ try {
1869
+ return this.get(import_sdk6.FrontMcpContextStorage)?.getStore()?.authInfo ?? {};
1870
+ } catch {
1871
+ return {};
1872
+ }
1649
1873
  }
1650
1874
  warnIfInsecureConfig() {
1651
1875
  if (this.options.dev) {
@@ -1667,15 +1891,15 @@ ${catalog}` : searchSkillDescription;
1667
1891
  { name: "skilled-openapi:hidden-ops", provide: HiddenOpRegistry, useValue: new HiddenOpRegistry() },
1668
1892
  {
1669
1893
  name: "skilled-openapi:bundle-store",
1670
- provide: import_skills6.BundleStore,
1894
+ provide: import_skills8.BundleStore,
1671
1895
  // Resolve TelemetryAccessor from the scope's provider registry so the
1672
1896
  // bundle-pulls counter and `skill.bundle.swap` span actually export.
1673
1897
  // The accessor is structurally compatible with `BundleStoreTelemetry`.
1674
1898
  // ObservabilityPlugin is an optional peer dep — when it isn't installed
1675
1899
  // we resolve `undefined` and the BundleStore falls through its zero-cost
1676
1900
  // no-telemetry path.
1677
- inject: () => [import_sdk5.ScopeEntry],
1678
- useFactory: (scope) => new import_skills6.BundleStore({ telemetry: resolveBundleTelemetry(scope) })
1901
+ inject: () => [import_sdk6.ScopeEntry],
1902
+ useFactory: (scope) => new import_skills8.BundleStore({ telemetry: resolveBundleTelemetry(scope) })
1679
1903
  },
1680
1904
  {
1681
1905
  name: "skilled-openapi:credential-resolver",
@@ -1685,13 +1909,23 @@ ${catalog}` : searchSkillDescription;
1685
1909
  {
1686
1910
  name: "skilled-openapi:authority-guard",
1687
1911
  provide: AuthorityGuard,
1688
- inject: () => [import_sdk5.ScopeEntry],
1689
- useFactory: (scope) => new AuthorityGuard({ logger: scope.logger.child("skilled-openapi:authority") })
1912
+ inject: () => [import_sdk6.ScopeEntry],
1913
+ useFactory: (scope) => new AuthorityGuard({
1914
+ logger: scope.logger.child("skilled-openapi:authority"),
1915
+ // Bundle rules follow the server's authorities settings (claimsMapping,
1916
+ // claimsResolver, relationshipResolver, evaluators) when it has them. Read per check:
1917
+ // the server may register its engine after this provider is built.
1918
+ serverAuthorities: () => {
1919
+ const engine = scope.authoritiesEngine;
1920
+ const contextBuilder = scope.authoritiesContextBuilder;
1921
+ return engine && contextBuilder ? { engine, contextBuilder } : void 0;
1922
+ }
1923
+ })
1690
1924
  },
1691
1925
  {
1692
1926
  name: "skilled-openapi:bundle-sync",
1693
1927
  provide: BundleSyncService,
1694
- inject: () => [import_sdk5.ScopeEntry, HiddenOpRegistry, import_skills6.BundleStore],
1928
+ inject: () => [import_sdk6.ScopeEntry, HiddenOpRegistry, import_skills8.BundleStore],
1695
1929
  useFactory: async (scope, hiddenOps, bundleStore) => {
1696
1930
  const logger = scope.logger.child("skilled-openapi:sync");
1697
1931
  const lazySkillRegistry = new Proxy({}, {
@@ -1749,7 +1983,7 @@ ${catalog}` : searchSkillDescription;
1749
1983
  const runtimeDeps = resolveRuntimeDeps(scope);
1750
1984
  let source;
1751
1985
  try {
1752
- source = (0, import_skills6.createBundleSource)(parsed.source, parsed.bundleCacheDir, logger, runtimeDeps);
1986
+ source = (0, import_skills8.createBundleSource)(parsed.source, parsed.bundleCacheDir, logger, runtimeDeps);
1753
1987
  } catch (e) {
1754
1988
  logger.error(`failed to construct bundle source: ${e.message}`);
1755
1989
  return sync;
@@ -1774,10 +2008,13 @@ ${catalog}` : searchSkillDescription;
1774
2008
  }
1775
2009
  };
1776
2010
  __decorateClass([
1777
- import_sdk5.ListToolsHook.Did("findTools", { priority: 50 })
2011
+ import_sdk6.ListToolsHook.Did("findTools", { priority: 50 })
1778
2012
  ], SkilledOpenApiPlugin.prototype, "injectSkillCatalogIntoSearchTool", 1);
2013
+ __decorateClass([
2014
+ FilterSkillsHook.Did("filterSkills", { priority: 50 })
2015
+ ], SkilledOpenApiPlugin.prototype, "hideBundleSkillsFromCaller", 1);
1779
2016
  SkilledOpenApiPlugin = __decorateClass([
1780
- (0, import_sdk5.Plugin)({
2017
+ (0, import_sdk6.Plugin)({
1781
2018
  name: "skilled-openapi",
1782
2019
  description: "Serve a customer's OpenAPI spec as signed skill bundles with hidden per-operation tools mediated by 3 meta-tools.",
1783
2020
  providers: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-skilled-openapi",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
4
4
  "description": "FrontMCP plugin: serve a customer's OpenAPI spec as skill bundles (signed, OpenAPI-Overlay-based) with hidden per-operation tools mediated by three meta-tools (search_skill / load_skill / run_workflow — the last runs an enclave-sandboxed AgentScript that orchestrates the loaded skills' operations).",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -48,15 +48,15 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@frontmcp/adapters": "1.8.2",
52
- "@frontmcp/auth": "1.8.2",
53
- "@frontmcp/sdk": "1.8.2",
54
- "@frontmcp/utils": "1.8.2"
51
+ "@frontmcp/adapters": "1.8.3",
52
+ "@frontmcp/auth": "1.8.3",
53
+ "@frontmcp/sdk": "1.8.3",
54
+ "@frontmcp/utils": "1.8.3"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@enclave-vm/ast": "*",
58
58
  "@enclave-vm/core": "*",
59
- "@frontmcp/lazy-zod": "1.8.2"
59
+ "@frontmcp/lazy-zod": "1.8.3"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "@enclave-vm/ast": {
@@ -1,8 +1,23 @@
1
1
  import type { AuthoritiesPolicy } from '@frontmcp/adapters/skills';
2
- import { AuthoritiesEvaluatorRegistry, AuthoritiesProfileRegistry, type AuthoritiesResult } from '@frontmcp/auth';
2
+ import { AuthoritiesContextBuilder, AuthoritiesEngine, AuthoritiesEvaluatorRegistry, AuthoritiesProfileRegistry, type AuthoritiesResult } from '@frontmcp/auth';
3
3
  import type { FrontMcpLogger } from '@frontmcp/sdk';
4
4
  /** How an op with NO authorities policy at all is treated by {@link AuthorityGuard}. */
5
5
  export type UnprotectedOpsPolicy = 'allow' | 'deny';
6
+ /** The server's authorities engine and context builder, when the server configures authorities. */
7
+ export interface ServerAuthorities {
8
+ engine: AuthoritiesEngine;
9
+ contextBuilder: AuthoritiesContextBuilder;
10
+ }
11
+ /** What {@link AuthorityGuard.canDiscover} needs: the rules of an action, without its input. */
12
+ export type DiscoveryCheckArgs = Omit<AuthorityCheckArgs, 'input' | 'env' | 'policy'> & {
13
+ policy?: AuthoritiesPolicy;
14
+ };
15
+ /**
16
+ * Whether a policy reads the action's input (or runs code the guard can't see
17
+ * into): `input.*` paths or keys, `fromInput` references, `custom` evaluators and
18
+ * `guards`. Such a policy can't be judged before the action is called.
19
+ */
20
+ export declare function policyDependsOnInput(value: unknown): boolean;
6
21
  export interface AuthorityCheckArgs {
7
22
  /** Op-level required-authorities policy from the bundle (`OperationDescriptor.requiredAuthorities`). */
8
23
  policy: AuthoritiesPolicy | undefined;
@@ -54,11 +69,29 @@ export declare class AuthorityGuard {
54
69
  private readonly engine;
55
70
  private readonly contextBuilder;
56
71
  private readonly logger;
72
+ private readonly serverAuthorities;
57
73
  constructor(opts?: {
58
74
  profiles?: AuthoritiesProfileRegistry;
59
75
  evaluators?: AuthoritiesEvaluatorRegistry;
60
76
  logger?: FrontMcpLogger;
77
+ /**
78
+ * The server's authorities, read on every check (the server may register
79
+ * them after this guard is built). When it returns an engine and context
80
+ * builder, they replace the guard's own defaults.
81
+ */
82
+ serverAuthorities?: () => ServerAuthorities | undefined;
61
83
  });
84
+ /** The server's engine and context builder when it configures authorities, else the guard's own. */
85
+ private authorities;
86
+ /**
87
+ * Whether the caller may be shown a skill or action (in `search_skill`,
88
+ * `load_skill`, the catalog and the SDK skill surfaces), judged without the
89
+ * action's input. A rule that depends on the input can't be judged yet and
90
+ * doesn't hide anything; the call is still checked by {@link check}. Every other
91
+ * rule must grant, and a policy-less action under `unprotectedOps: 'deny'` must
92
+ * be public, exactly as at call time.
93
+ */
94
+ canDiscover(args: DiscoveryCheckArgs): Promise<boolean>;
62
95
  check(args: AuthorityCheckArgs): Promise<AuthoritiesResult>;
63
96
  }
64
97
  //# sourceMappingURL=authority-guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authority-guard.d.ts","sourceRoot":"","sources":["../../src/security/authority-guard.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAGL,4BAA4B,EAC5B,0BAA0B,EAG1B,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,wFAAwF;AACxF,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,wGAAwG;IACxG,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACvF,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;gBAGlD,IAAI,GAAE;QACJ,QAAQ,CAAC,EAAE,0BAA0B,CAAC;QACtC,UAAU,CAAC,EAAE,4BAA4B,CAAC;QAC1C,MAAM,CAAC,EAAE,cAAc,CAAC;KACpB;IASF,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAsDlE"}
1
+ {"version":3,"file":"authority-guard.d.ts","sourceRoot":"","sources":["../../src/security/authority-guard.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,4BAA4B,EAC5B,0BAA0B,EAG1B,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,wFAAwF;AACxF,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpD,mGAAmG;AACnG,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,cAAc,EAAE,yBAAyB,CAAC;CAC3C;AAED,gGAAgG;AAChG,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,GAAG;IACtF,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAU5D;AAwBD,MAAM,WAAW,kBAAkB;IACjC,wGAAwG;IACxG,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACvF,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoD;gBAGpF,IAAI,GAAE;QACJ,QAAQ,CAAC,EAAE,0BAA0B,CAAC;QACtC,UAAU,CAAC,EAAE,4BAA4B,CAAC;QAC1C,MAAM,CAAC,EAAE,cAAc,CAAC;QACxB;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,MAAM,iBAAiB,GAAG,SAAS,CAAC;KACpD;IAUR,oGAAoG;IACpG,OAAO,CAAC,WAAW;IAInB;;;;;;;OAOG;IACG,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAWvD,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAuDlE"}
@@ -0,0 +1,46 @@
1
+ import type { AuthoritiesPolicy, ResolvedBundle } from '@frontmcp/adapters/skills';
2
+ import { type ScopeEntry, type SkillAction, type SkillEntry } from '@frontmcp/sdk';
3
+ import type { HiddenOpRegistry } from '../registry/hidden-op.registry';
4
+ import type { AuthorityGuard, UnprotectedOpsPolicy } from './authority-guard';
5
+ export interface SkillVisibilityDeps {
6
+ scope: ScopeEntry;
7
+ guard: AuthorityGuard;
8
+ /** The active bundle, whose skill-level `requiredAuthorities` gate its skills. */
9
+ bundle: ResolvedBundle | undefined;
10
+ unprotectedOps: UnprotectedOpsPolicy;
11
+ /** The caller's AuthInfo. */
12
+ authInfo: unknown;
13
+ }
14
+ /** The skill-level rule of each skill in `bundle` that has one. */
15
+ export declare function bundleSkillPolicies(bundle: ResolvedBundle | undefined): Map<string, AuthoritiesPolicy>;
16
+ export declare class SkillVisibility {
17
+ private readonly deps;
18
+ private readonly policies;
19
+ private readonly authInfo;
20
+ constructor(deps: SkillVisibilityDeps);
21
+ /** Whether the caller satisfies the bundle skill's own `requiredAuthorities` (true for other skills). */
22
+ passesBundleSkillRule(skillId: string): Promise<boolean>;
23
+ /**
24
+ * Keep the items whose skill the caller may see: the bundle skill's rule, the
25
+ * `skills:filter` flow and the server's `@Skill({ authorities })` rule. An item
26
+ * whose id names no registered skill (an external skill) is judged by the bundle
27
+ * rule alone, as the SDK's own surfaces keep such results.
28
+ */
29
+ filterVisible<T>(items: readonly T[], idOf: (item: T) => string): Promise<T[]>;
30
+ /** The registered skills the caller may see, in their order (see {@link filterVisible}). */
31
+ filterVisibleEntries<T extends SkillEntry>(entries: readonly T[]): Promise<T[]>;
32
+ /** Whether the caller may see the skill named by `skillId` (see {@link filterVisible}). */
33
+ isVisible(skillId: string): Promise<boolean>;
34
+ /**
35
+ * The actions of a bundle skill the caller may be shown: an action whose rules
36
+ * refuse the caller whatever the input (or that `unprotectedOps: 'deny'` blocks)
37
+ * is left out. Rules that depend on the input keep the action listed; the call is
38
+ * checked again when it runs.
39
+ */
40
+ visibleActions(skillId: string, actions: readonly SkillAction[], hiddenOps: HiddenOpRegistry): Promise<SkillAction[]>;
41
+ /** The server's `@Skill({ authorities })` rule, evaluated as the SDK's skill surfaces do. */
42
+ private passesSkillAuthorities;
43
+ /** The registered skill an id names, looked up the way the SDK's skill surfaces do. */
44
+ private resolveEntry;
45
+ }
46
+ //# sourceMappingURL=skill-visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-visibility.d.ts","sourceRoot":"","sources":["../../src/security/skill-visibility.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEnF,OAAO,EAAwB,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAEzG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,kFAAkF;IAClF,MAAM,EAAE,cAAc,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,oBAAoB,CAAC;IACrC,6BAA6B;IAC7B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAQtG;AAED,qBAAa,eAAe;IAId,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;gBAEb,IAAI,EAAE,mBAAmB;IAKtD,yGAAyG;IACnG,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM9D;;;;;OAKG;IACG,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAepF,4FAA4F;IACtF,oBAAoB,CAAC,CAAC,SAAS,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAYrF,2FAA2F;IACrF,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlD;;;;;OAKG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,SAAS,EAAE,gBAAgB;IAiBlG,6FAA6F;YAC/E,sBAAsB;IAepC,uFAAuF;IACvF,OAAO,CAAC,YAAY;CASrB"}
@@ -57,6 +57,16 @@ export default class SkilledOpenApiPlugin extends DynamicPlugin<SkilledOpenApiPl
57
57
  * from the static base each time, never appended onto a prior catalog.
58
58
  */
59
59
  injectSkillCatalogIntoSearchTool(flowCtx: FlowCtxOf<'tools:list-tools'>): Promise<void>;
60
+ /**
61
+ * Hide a bundle skill from a caller who doesn't satisfy its `requiredAuthorities`
62
+ * on every SDK skill surface (skills/list, skills/search, skills/load, the
63
+ * `skill://` resources, the HTTP /skills API): a skill this flow drops is absent
64
+ * there and not found when named. The plugin's own meta-tools apply the same rule.
65
+ */
66
+ hideBundleSkillsFromCaller(flowCtx: FlowCtxOf<'skills:filter'>): Promise<void>;
67
+ private skillVisibility;
68
+ /** The AuthInfo of the request being served; an anonymous caller when there is none. */
69
+ private currentAuthInfo;
60
70
  private warnIfInsecureConfig;
61
71
  static dynamicProviders(options: SkilledOpenApiPluginOptionsInput): ProviderType[];
62
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skilled-openapi.plugin.d.ts","sourceRoot":"","sources":["../src/skilled-openapi.plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EAIrB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,aAAa,EAKb,KAAK,SAAS,EAEd,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAMvB,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,EACtC,MAAM,yBAAyB,CAAC;AAmBjC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kCAAkC,eAAsD,CAAC;AAEtG;;;;;GAKG;AACH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE;;;;OAIG;IACH,MAAM,CAAC,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C;AAqDD;;;;;;;;;;;;GAYG;AAQH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,aAAa,CAC7D,2BAA2B,EAC3B,gCAAgC,CACjC;IACC,OAAO,EAAE,2BAA2B,CAAC;IACrC,OAAO,CAAC,YAAY,CAAC,CAAiB;gBAE1B,OAAO,EAAE,gCAAgC;IAMrD,OAAO,CAAC,SAAS;IAOjB;;;;;;;;;;OAUG;IAEG,gCAAgC,CAAC,OAAO,EAAE,SAAS,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB7F,OAAO,CAAC,oBAAoB;WAaZ,gBAAgB,CAAC,OAAO,EAAE,gCAAgC,GAAG,YAAY,EAAE;CA0J5F"}
1
+ {"version":3,"file":"skilled-openapi.plugin.d.ts","sourceRoot":"","sources":["../src/skilled-openapi.plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EAIrB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,aAAa,EAOb,KAAK,SAAS,EAEd,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAOvB,OAAO,EAEL,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,EACtC,MAAM,yBAAyB,CAAC;AAsBjC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kCAAkC,eAAsD,CAAC;AAEtG;;;;;GAKG;AACH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE;;;;OAIG;IACH,MAAM,CAAC,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC1C;AAqDD;;;;;;;;;;;;GAYG;AAQH,MAAM,CAAC,OAAO,OAAO,oBAAqB,SAAQ,aAAa,CAC7D,2BAA2B,EAC3B,gCAAgC,CACjC;IACC,OAAO,EAAE,2BAA2B,CAAC;IACrC,OAAO,CAAC,YAAY,CAAC,CAAiB;gBAE1B,OAAO,EAAE,gCAAgC;IAMrD,OAAO,CAAC,SAAS;IAOjB;;;;;;;;;;OAUG;IAEG,gCAAgC,CAAC,OAAO,EAAE,SAAS,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC7F;;;;;OAKG;IAEG,0BAA0B,CAAC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBpF,OAAO,CAAC,eAAe;IAUvB,wFAAwF;IACxF,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,oBAAoB;WAaZ,gBAAgB,CAAC,OAAO,EAAE,gCAAgC,GAAG,YAAY,EAAE;CAoK5F"}
@@ -1 +1 @@
1
- {"version":3,"file":"load-skill.tool.d.ts","sourceRoot":"","sources":["../../src/tools/load-skill.tool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsD,WAAW,EAAE,MAAM,eAAe,CAAC;AAGhG,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAY7B,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IAC9C,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAgC/D"}
1
+ {"version":3,"file":"load-skill.tool.d.ts","sourceRoot":"","sources":["../../src/tools/load-skill.tool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsD,WAAW,EAAE,MAAM,eAAe,CAAC;AAOhG,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAY7B,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IAC9C,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAkD/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"search-skill.tool.d.ts","sourceRoot":"","sources":["../../src/tools/search-skill.tool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,WAAW,EAAE,MAAM,eAAe,CAAC;AAG9D,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAY/B,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;IAChD,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAkCnE"}
1
+ {"version":3,"file":"search-skill.tool.d.ts","sourceRoot":"","sources":["../../src/tools/search-skill.tool.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,WAAW,EAAE,MAAM,eAAe,CAAC;AAM9D,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAY/B,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;IAChD,OAAO,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA8CnE"}