Merge pull request #294 from matteo8p/create-helper-functions-tool-visibility
Add `isToolVisibilityModelOnly` and `isToolVisibilityAppOnly` helper functions
This commit is contained in:
commit
398f5b4e4a
2 changed files with 182 additions and 1 deletions
|
|
@ -17,6 +17,8 @@ import { App } from "./app";
|
||||||
import {
|
import {
|
||||||
AppBridge,
|
AppBridge,
|
||||||
getToolUiResourceUri,
|
getToolUiResourceUri,
|
||||||
|
isToolVisibilityModelOnly,
|
||||||
|
isToolVisibilityAppOnly,
|
||||||
type McpUiHostCapabilities,
|
type McpUiHostCapabilities,
|
||||||
} from "./app-bridge";
|
} from "./app-bridge";
|
||||||
|
|
||||||
|
|
@ -921,3 +923,153 @@ describe("getToolUiResourceUri", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("isToolVisibilityModelOnly", () => {
|
||||||
|
describe("returns true", () => {
|
||||||
|
it("when visibility is exactly ['model']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["model"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("returns false", () => {
|
||||||
|
it("when visibility is ['app']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["app"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is ['model', 'app']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["model", "app"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is ['app', 'model']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["app", "model"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is empty array", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: [] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is undefined", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: {} },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when _meta.ui is missing", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: {},
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when _meta is missing", () => {
|
||||||
|
const tool = { name: "test-tool" };
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when tool has resourceUri but no visibility", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { resourceUri: "ui://server/app.html" } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityModelOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isToolVisibilityAppOnly", () => {
|
||||||
|
describe("returns true", () => {
|
||||||
|
it("when visibility is exactly ['app']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["app"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("returns false", () => {
|
||||||
|
it("when visibility is ['model']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["model"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is ['model', 'app']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["model", "app"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is ['app', 'model']", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: ["app", "model"] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is empty array", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { visibility: [] } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when visibility is undefined", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: {} },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when _meta.ui is missing", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: {},
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when _meta is missing", () => {
|
||||||
|
const tool = { name: "test-tool" };
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when tool has resourceUri but no visibility", () => {
|
||||||
|
const tool = {
|
||||||
|
name: "test-tool",
|
||||||
|
_meta: { ui: { resourceUri: "ui://server/app.html" } },
|
||||||
|
};
|
||||||
|
expect(isToolVisibilityAppOnly(tool)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ import {
|
||||||
McpUiRequestDisplayModeRequestSchema,
|
McpUiRequestDisplayModeRequestSchema,
|
||||||
McpUiRequestDisplayModeResult,
|
McpUiRequestDisplayModeResult,
|
||||||
McpUiResourcePermissions,
|
McpUiResourcePermissions,
|
||||||
|
McpUiToolMeta,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
export * from "./types";
|
export * from "./types";
|
||||||
export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./app";
|
export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./app";
|
||||||
|
|
@ -110,7 +111,7 @@ export { PostMessageTransport } from "./message-transport";
|
||||||
*/
|
*/
|
||||||
export function getToolUiResourceUri(tool: Partial<Tool>): string | undefined {
|
export function getToolUiResourceUri(tool: Partial<Tool>): string | undefined {
|
||||||
// Try new nested format first: _meta.ui.resourceUri
|
// Try new nested format first: _meta.ui.resourceUri
|
||||||
const uiMeta = tool._meta?.ui as { resourceUri?: unknown } | undefined;
|
const uiMeta = tool._meta?.ui as McpUiToolMeta | undefined;
|
||||||
let uri: unknown = uiMeta?.resourceUri;
|
let uri: unknown = uiMeta?.resourceUri;
|
||||||
|
|
||||||
// Fall back to deprecated flat format: _meta["ui/resourceUri"]
|
// Fall back to deprecated flat format: _meta["ui/resourceUri"]
|
||||||
|
|
@ -126,6 +127,34 @@ export function getToolUiResourceUri(tool: Partial<Tool>): string | undefined {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a tool is visible to the model only.
|
||||||
|
*
|
||||||
|
* @param tool - Tool object with visibility metadata
|
||||||
|
* @returns True if the tool is visible to the model only, false otherwise
|
||||||
|
*/
|
||||||
|
export function isToolVisibilityModelOnly(tool: Partial<Tool>): boolean {
|
||||||
|
const uiMeta = tool._meta?.ui as McpUiToolMeta | undefined;
|
||||||
|
const visibility = uiMeta?.visibility as Array<"model" | "app"> | undefined;
|
||||||
|
if (!visibility) return false;
|
||||||
|
if (visibility.length === 1 && visibility[0] === "model") return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a tool is visible to the app only.
|
||||||
|
*
|
||||||
|
* @param tool - Tool object with visibility metadata
|
||||||
|
* @returns True if the tool is visible to the app only, false otherwise
|
||||||
|
*/
|
||||||
|
export function isToolVisibilityAppOnly(tool: Partial<Tool>): boolean {
|
||||||
|
const uiMeta = tool._meta?.ui as McpUiToolMeta | undefined;
|
||||||
|
const visibility = uiMeta?.visibility as Array<"model" | "app"> | undefined;
|
||||||
|
if (!visibility) return false;
|
||||||
|
if (visibility.length === 1 && visibility[0] === "app") return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build iframe `allow` attribute string from permissions.
|
* Build iframe `allow` attribute string from permissions.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue