Replace deprecated RESOURCE_URI_META_KEY usage with modern format (#457)
This commit is contained in:
parent
ef8fc6e2f0
commit
19fe73a08a
4 changed files with 80 additions and 28 deletions
|
|
@ -17,7 +17,6 @@ import {
|
||||||
registerAppTool,
|
registerAppTool,
|
||||||
registerAppResource,
|
registerAppResource,
|
||||||
RESOURCE_MIME_TYPE,
|
RESOURCE_MIME_TYPE,
|
||||||
RESOURCE_URI_META_KEY,
|
|
||||||
} from "@modelcontextprotocol/ext-apps/server";
|
} from "@modelcontextprotocol/ext-apps/server";
|
||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
|
|
||||||
|
|
@ -25,7 +24,7 @@ import { randomUUID } from "crypto";
|
||||||
const DIST_DIR = import.meta.filename.endsWith(".ts")
|
const DIST_DIR = import.meta.filename.endsWith(".ts")
|
||||||
? path.join(import.meta.dirname, "dist")
|
? path.join(import.meta.dirname, "dist")
|
||||||
: import.meta.dirname;
|
: import.meta.dirname;
|
||||||
const RESOURCE_URI = "ui://cesium-map/mcp-app.html";
|
const resourceUri = "ui://cesium-map/mcp-app.html";
|
||||||
|
|
||||||
// Nominatim API response type
|
// Nominatim API response type
|
||||||
interface NominatimResult {
|
interface NominatimResult {
|
||||||
|
|
@ -118,8 +117,8 @@ export function createServer(): McpServer {
|
||||||
// Register the CesiumJS map resource with CSP for external tile sources
|
// Register the CesiumJS map resource with CSP for external tile sources
|
||||||
registerAppResource(
|
registerAppResource(
|
||||||
server,
|
server,
|
||||||
RESOURCE_URI,
|
resourceUri,
|
||||||
RESOURCE_URI,
|
resourceUri,
|
||||||
{ mimeType: RESOURCE_MIME_TYPE },
|
{ mimeType: RESOURCE_MIME_TYPE },
|
||||||
async (): Promise<ReadResourceResult> => {
|
async (): Promise<ReadResourceResult> => {
|
||||||
const html = await fs.readFile(
|
const html = await fs.readFile(
|
||||||
|
|
@ -130,7 +129,7 @@ export function createServer(): McpServer {
|
||||||
contents: [
|
contents: [
|
||||||
// CSP metadata on the content item takes precedence over listing-level _meta
|
// CSP metadata on the content item takes precedence over listing-level _meta
|
||||||
{
|
{
|
||||||
uri: RESOURCE_URI,
|
uri: resourceUri,
|
||||||
mimeType: RESOURCE_MIME_TYPE,
|
mimeType: RESOURCE_MIME_TYPE,
|
||||||
text: html,
|
text: html,
|
||||||
_meta: cspMeta,
|
_meta: cspMeta,
|
||||||
|
|
@ -175,7 +174,7 @@ export function createServer(): McpServer {
|
||||||
.optional()
|
.optional()
|
||||||
.describe("Optional label to display on the map"),
|
.describe("Optional label to display on the map"),
|
||||||
},
|
},
|
||||||
_meta: { [RESOURCE_URI_META_KEY]: RESOURCE_URI },
|
_meta: { ui: { resourceUri } },
|
||||||
},
|
},
|
||||||
async ({ west, south, east, north, label }): Promise<CallToolResult> => ({
|
async ({ west, south, east, north, label }): Promise<CallToolResult> => ({
|
||||||
content: [
|
content: [
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,12 @@ import {
|
||||||
registerAppTool,
|
registerAppTool,
|
||||||
registerAppResource,
|
registerAppResource,
|
||||||
RESOURCE_MIME_TYPE,
|
RESOURCE_MIME_TYPE,
|
||||||
RESOURCE_URI_META_KEY,
|
|
||||||
} from "@modelcontextprotocol/ext-apps/server";
|
} from "@modelcontextprotocol/ext-apps/server";
|
||||||
// Works both from source (server.ts) and compiled (dist/server.js)
|
// Works both from source (server.ts) and compiled (dist/server.js)
|
||||||
const DIST_DIR = import.meta.filename.endsWith(".ts")
|
const DIST_DIR = import.meta.filename.endsWith(".ts")
|
||||||
? path.join(import.meta.dirname, "dist")
|
? path.join(import.meta.dirname, "dist")
|
||||||
: import.meta.dirname;
|
: import.meta.dirname;
|
||||||
const RESOURCE_URI = "ui://transcript/mcp-app.html";
|
const resourceUri = "ui://transcript/mcp-app.html";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new MCP server instance with tools and resources registered.
|
* Creates a new MCP server instance with tools and resources registered.
|
||||||
|
|
@ -35,7 +34,7 @@ export function createServer(): McpServer {
|
||||||
description:
|
description:
|
||||||
"Opens a live speech transcription interface using the Web Speech API.",
|
"Opens a live speech transcription interface using the Web Speech API.",
|
||||||
inputSchema: {},
|
inputSchema: {},
|
||||||
_meta: { [RESOURCE_URI_META_KEY]: RESOURCE_URI },
|
_meta: { ui: { resourceUri } },
|
||||||
},
|
},
|
||||||
async (): Promise<CallToolResult> => {
|
async (): Promise<CallToolResult> => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -55,8 +54,8 @@ export function createServer(): McpServer {
|
||||||
// Register the UI resource
|
// Register the UI resource
|
||||||
registerAppResource(
|
registerAppResource(
|
||||||
server,
|
server,
|
||||||
RESOURCE_URI,
|
resourceUri,
|
||||||
RESOURCE_URI,
|
resourceUri,
|
||||||
{ mimeType: RESOURCE_MIME_TYPE, description: "Transcript UI" },
|
{ mimeType: RESOURCE_MIME_TYPE, description: "Transcript UI" },
|
||||||
async (): Promise<ReadResourceResult> => {
|
async (): Promise<ReadResourceResult> => {
|
||||||
const html = await fs.readFile(
|
const html = await fs.readFile(
|
||||||
|
|
@ -67,7 +66,7 @@ export function createServer(): McpServer {
|
||||||
return {
|
return {
|
||||||
contents: [
|
contents: [
|
||||||
{
|
{
|
||||||
uri: RESOURCE_URI,
|
uri: resourceUri,
|
||||||
mimeType: RESOURCE_MIME_TYPE,
|
mimeType: RESOURCE_MIME_TYPE,
|
||||||
text: html,
|
text: html,
|
||||||
_meta: {
|
_meta: {
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,46 @@ import type {
|
||||||
McpServer,
|
McpServer,
|
||||||
ToolCallback,
|
ToolCallback,
|
||||||
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import { App, PostMessageTransport, RESOURCE_URI_META_KEY } from "./app.js";
|
import {
|
||||||
|
App,
|
||||||
|
PostMessageTransport,
|
||||||
|
RESOURCE_URI_META_KEY,
|
||||||
|
McpUiToolMeta,
|
||||||
|
} from "./app.js";
|
||||||
|
import { registerAppTool } from "./server/index.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: How MCP servers use RESOURCE_URI_META_KEY (server-side, not in Apps).
|
* Example: Modern format for registering tools with UI (recommended).
|
||||||
*/
|
*/
|
||||||
function RESOURCE_URI_META_KEY_serverSide(
|
function RESOURCE_URI_META_KEY_modernFormat(
|
||||||
server: McpServer,
|
server: McpServer,
|
||||||
handler: ToolCallback,
|
handler: ToolCallback,
|
||||||
) {
|
) {
|
||||||
//#region RESOURCE_URI_META_KEY_serverSide
|
//#region RESOURCE_URI_META_KEY_modernFormat
|
||||||
|
// Preferred: Use registerAppTool with nested ui.resourceUri
|
||||||
|
registerAppTool(
|
||||||
|
server,
|
||||||
|
"weather",
|
||||||
|
{
|
||||||
|
description: "Get weather forecast",
|
||||||
|
_meta: {
|
||||||
|
ui: { resourceUri: "ui://weather/forecast" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
handler,
|
||||||
|
);
|
||||||
|
//#endregion RESOURCE_URI_META_KEY_modernFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Legacy format using RESOURCE_URI_META_KEY (deprecated).
|
||||||
|
*/
|
||||||
|
function RESOURCE_URI_META_KEY_legacyFormat(
|
||||||
|
server: McpServer,
|
||||||
|
handler: ToolCallback,
|
||||||
|
) {
|
||||||
|
//#region RESOURCE_URI_META_KEY_legacyFormat
|
||||||
|
// Deprecated: Direct use of RESOURCE_URI_META_KEY
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
"weather",
|
"weather",
|
||||||
{
|
{
|
||||||
|
|
@ -32,16 +62,19 @@ function RESOURCE_URI_META_KEY_serverSide(
|
||||||
},
|
},
|
||||||
handler,
|
handler,
|
||||||
);
|
);
|
||||||
//#endregion RESOURCE_URI_META_KEY_serverSide
|
//#endregion RESOURCE_URI_META_KEY_legacyFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example: How hosts check for RESOURCE_URI_META_KEY metadata (host-side).
|
* Example: How hosts check for RESOURCE_URI_META_KEY metadata (must support both formats).
|
||||||
*/
|
*/
|
||||||
function RESOURCE_URI_META_KEY_hostSide(tool: Tool) {
|
function RESOURCE_URI_META_KEY_hostSide(tool: Tool) {
|
||||||
//#region RESOURCE_URI_META_KEY_hostSide
|
//#region RESOURCE_URI_META_KEY_hostSide
|
||||||
// Check tool definition metadata (from tools/list response):
|
// Hosts should check both modern and legacy formats
|
||||||
const uiUri = tool._meta?.[RESOURCE_URI_META_KEY];
|
const meta = tool._meta;
|
||||||
|
const uiMeta = meta?.ui as McpUiToolMeta | undefined;
|
||||||
|
const legacyUri = meta?.[RESOURCE_URI_META_KEY] as string | undefined;
|
||||||
|
const uiUri = uiMeta?.resourceUri ?? legacyUri;
|
||||||
if (typeof uiUri === "string" && uiUri.startsWith("ui://")) {
|
if (typeof uiUri === "string" && uiUri.startsWith("ui://")) {
|
||||||
// Fetch the resource and display the UI
|
// Fetch the resource and display the UI
|
||||||
}
|
}
|
||||||
|
|
|
||||||
37
src/app.ts
37
src/app.ts
|
|
@ -75,12 +75,30 @@ export {
|
||||||
* When hosts see a tool with this metadata, they fetch and render the
|
* When hosts see a tool with this metadata, they fetch and render the
|
||||||
* corresponding {@link App `App`}.
|
* corresponding {@link App `App`}.
|
||||||
*
|
*
|
||||||
* **Note**: This constant is provided for reference. App developers typically
|
* **Note**: This constant is provided for reference and backwards compatibility.
|
||||||
* don't need to use it directly. Prefer using {@link server-helpers!registerAppTool `registerAppTool`}
|
* Server developers should use {@link server-helpers!registerAppTool `registerAppTool`}
|
||||||
* with the `_meta.ui.resourceUri` format instead.
|
* with the `_meta.ui.resourceUri` format instead. Host developers must check both
|
||||||
|
* formats for compatibility.
|
||||||
*
|
*
|
||||||
* @example How MCP servers use this key (server-side, not in Apps)
|
* @example Modern format (server-side, not in Apps)
|
||||||
* ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_serverSide"
|
* ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_modernFormat"
|
||||||
|
* // Preferred: Use registerAppTool with nested ui.resourceUri
|
||||||
|
* registerAppTool(
|
||||||
|
* server,
|
||||||
|
* "weather",
|
||||||
|
* {
|
||||||
|
* description: "Get weather forecast",
|
||||||
|
* _meta: {
|
||||||
|
* ui: { resourceUri: "ui://weather/forecast" },
|
||||||
|
* },
|
||||||
|
* },
|
||||||
|
* handler,
|
||||||
|
* );
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example Legacy format (deprecated, for backwards compatibility)
|
||||||
|
* ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_legacyFormat"
|
||||||
|
* // Deprecated: Direct use of RESOURCE_URI_META_KEY
|
||||||
* server.registerTool(
|
* server.registerTool(
|
||||||
* "weather",
|
* "weather",
|
||||||
* {
|
* {
|
||||||
|
|
@ -93,10 +111,13 @@ export {
|
||||||
* );
|
* );
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @example How hosts check for this metadata (host-side)
|
* @example How hosts check for this metadata (must support both formats)
|
||||||
* ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_hostSide"
|
* ```ts source="./app.examples.ts#RESOURCE_URI_META_KEY_hostSide"
|
||||||
* // Check tool definition metadata (from tools/list response):
|
* // Hosts should check both modern and legacy formats
|
||||||
* const uiUri = tool._meta?.[RESOURCE_URI_META_KEY];
|
* const meta = tool._meta;
|
||||||
|
* const uiMeta = meta?.ui as McpUiToolMeta | undefined;
|
||||||
|
* const legacyUri = meta?.[RESOURCE_URI_META_KEY] as string | undefined;
|
||||||
|
* const uiUri = uiMeta?.resourceUri ?? legacyUri;
|
||||||
* if (typeof uiUri === "string" && uiUri.startsWith("ui://")) {
|
* if (typeof uiUri === "string" && uiUri.startsWith("ui://")) {
|
||||||
* // Fetch the resource and display the UI
|
* // Fetch the resource and display the UI
|
||||||
* }
|
* }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue