@openzeppelin/contracts-mcp 0.6.1 → 0.6.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/apps/cairo.html +20 -0
- package/apps/confidential.html +20 -0
- package/apps/solidity.html +22 -0
- package/apps/stellar.html +20 -0
- package/apps/stylus.html +20 -0
- package/apps/uniswap-hooks.html +1 -1
- package/dist/apps/register.d.ts +19 -0
- package/dist/apps/register.d.ts.map +1 -1
- package/dist/apps/register.js +97 -9
- package/dist/apps/register.js.map +1 -1
- package/package.json +3 -3
- package/src/apps/register.ts +119 -11
- package/apps/cairo-account.html +0 -20
- package/apps/cairo-custom.html +0 -20
- package/apps/cairo-erc1155.html +0 -20
- package/apps/cairo-erc20.html +0 -20
- package/apps/cairo-erc721.html +0 -20
- package/apps/cairo-governor.html +0 -20
- package/apps/cairo-multisig.html +0 -20
- package/apps/cairo-vesting.html +0 -20
- package/apps/confidential-erc7984.html +0 -20
- package/apps/solidity-account.html +0 -22
- package/apps/solidity-custom.html +0 -22
- package/apps/solidity-erc1155.html +0 -22
- package/apps/solidity-erc20.html +0 -22
- package/apps/solidity-erc721.html +0 -22
- package/apps/solidity-governor.html +0 -22
- package/apps/solidity-rwa.html +0 -22
- package/apps/solidity-stablecoin.html +0 -22
- package/apps/stellar-fungible.html +0 -20
- package/apps/stellar-governor.html +0 -20
- package/apps/stellar-non-fungible.html +0 -20
- package/apps/stellar-stablecoin.html +0 -20
- package/apps/stellar-vault.html +0 -20
- package/apps/stylus-erc1155.html +0 -20
- package/apps/stylus-erc20.html +0 -20
- package/apps/stylus-erc721.html +0 -20
package/dist/apps/register.d.ts
CHANGED
|
@@ -2,7 +2,26 @@ import type { McpServer, RegisteredTool, ToolCallback } from '@modelcontextproto
|
|
|
2
2
|
import type { ZodRawShapeCompat } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
3
3
|
/** MIME type for MCP Apps HTML resources. */
|
|
4
4
|
export declare const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|
5
|
+
/**
|
|
6
|
+
* Kind placeholder baked into language HTML templates by the UI build.
|
|
7
|
+
* Duplicated in packages/ui (kind-placeholder.ts, package-mcp-apps.mjs); kept aligned by
|
|
8
|
+
* `kind placeholder stays in sync` in register.test.ts.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MCP_KIND_PLACEHOLDER = "__OZ_MCP_KIND__";
|
|
11
|
+
export type AppTemplate = 'solidity' | 'cairo' | 'stellar' | 'stylus' | 'confidential' | 'uniswap-hooks';
|
|
12
|
+
export type ToolAppSpec = {
|
|
13
|
+
template: AppTemplate;
|
|
14
|
+
kind: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Exhaustive tool → language template + kind map.
|
|
18
|
+
* Kept in lockstep with packages/core kinds by `TOOL_APP_SPECS matches every core kind and UI language entry`
|
|
19
|
+
* in server.test.ts — do not add tools here without that test (and the UI entries/<language>.ts) passing.
|
|
20
|
+
* Overrides match MCP_TOOL_NAME_OVERRIDES in server.test.ts: solidity-rwa ← RealWorldAsset, uniswap-hooks ← Hooks.
|
|
21
|
+
*/
|
|
22
|
+
export declare const TOOL_APP_SPECS: Readonly<Record<string, ToolAppSpec>>;
|
|
5
23
|
export declare function appResourceUri(toolName: string): string;
|
|
24
|
+
export declare function getToolAppSpec(toolName: string): ToolAppSpec;
|
|
6
25
|
export declare function readAppHtml(toolName: string): Promise<string>;
|
|
7
26
|
type AppToolConfig<Args extends ZodRawShapeCompat> = {
|
|
8
27
|
description: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/apps/register.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AACvG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AAGxF,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAM9D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/apps/register.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AACvG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gDAAgD,CAAC;AAGxF,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAM9D;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,cAAc,GAAG,eAAe,CAAC;AAEzG,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAgChE,CAAC;AAEF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAU5D;AAiED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB7D;AA2BD,KAAK,aAAa,CAAC,IAAI,SAAS,iBAAiB,IAAI;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,IAAI,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,SAAS,iBAAiB,EAClE,MAAM,EAAE,SAAS,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,EAC3B,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,GACrB,cAAc,CAoBhB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,EAAE,0BAA0B,EAAE,MAAM;;;;;;;;;;;EAO1G;AAED,iGAAiG;AACjG,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;;;;;;;;;;;EAevG"}
|
package/dist/apps/register.js
CHANGED
|
@@ -3,8 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.RESOURCE_MIME_TYPE = void 0;
|
|
6
|
+
exports.TOOL_APP_SPECS = exports.MCP_KIND_PLACEHOLDER = exports.RESOURCE_MIME_TYPE = void 0;
|
|
7
7
|
exports.appResourceUri = appResourceUri;
|
|
8
|
+
exports.getToolAppSpec = getToolAppSpec;
|
|
8
9
|
exports.readAppHtml = readAppHtml;
|
|
9
10
|
exports.registerWizardAppTool = registerWizardAppTool;
|
|
10
11
|
exports.wizardAppPrintResult = wizardAppPrintResult;
|
|
@@ -16,25 +17,92 @@ const utils_1 = require("../utils");
|
|
|
16
17
|
exports.RESOURCE_MIME_TYPE = 'text/html;profile=mcp-app';
|
|
17
18
|
const RESOURCE_URI_META_KEY = 'ui/resourceUri';
|
|
18
19
|
const APPS_DIR = path_1.default.join(__dirname, '..', '..', 'apps');
|
|
20
|
+
/**
|
|
21
|
+
* Kind placeholder baked into language HTML templates by the UI build.
|
|
22
|
+
* Duplicated in packages/ui (kind-placeholder.ts, package-mcp-apps.mjs); kept aligned by
|
|
23
|
+
* `kind placeholder stays in sync` in register.test.ts.
|
|
24
|
+
*/
|
|
25
|
+
exports.MCP_KIND_PLACEHOLDER = '__OZ_MCP_KIND__';
|
|
26
|
+
/**
|
|
27
|
+
* Exhaustive tool → language template + kind map.
|
|
28
|
+
* Kept in lockstep with packages/core kinds by `TOOL_APP_SPECS matches every core kind and UI language entry`
|
|
29
|
+
* in server.test.ts — do not add tools here without that test (and the UI entries/<language>.ts) passing.
|
|
30
|
+
* Overrides match MCP_TOOL_NAME_OVERRIDES in server.test.ts: solidity-rwa ← RealWorldAsset, uniswap-hooks ← Hooks.
|
|
31
|
+
*/
|
|
32
|
+
exports.TOOL_APP_SPECS = {
|
|
33
|
+
'solidity-erc20': { template: 'solidity', kind: 'ERC20' },
|
|
34
|
+
'solidity-erc721': { template: 'solidity', kind: 'ERC721' },
|
|
35
|
+
'solidity-erc1155': { template: 'solidity', kind: 'ERC1155' },
|
|
36
|
+
'solidity-stablecoin': { template: 'solidity', kind: 'Stablecoin' },
|
|
37
|
+
'solidity-rwa': { template: 'solidity', kind: 'RealWorldAsset' },
|
|
38
|
+
'solidity-account': { template: 'solidity', kind: 'Account' },
|
|
39
|
+
'solidity-governor': { template: 'solidity', kind: 'Governor' },
|
|
40
|
+
'solidity-custom': { template: 'solidity', kind: 'Custom' },
|
|
41
|
+
'cairo-erc20': { template: 'cairo', kind: 'ERC20' },
|
|
42
|
+
'cairo-erc721': { template: 'cairo', kind: 'ERC721' },
|
|
43
|
+
'cairo-erc1155': { template: 'cairo', kind: 'ERC1155' },
|
|
44
|
+
'cairo-account': { template: 'cairo', kind: 'Account' },
|
|
45
|
+
'cairo-multisig': { template: 'cairo', kind: 'Multisig' },
|
|
46
|
+
'cairo-governor': { template: 'cairo', kind: 'Governor' },
|
|
47
|
+
'cairo-vesting': { template: 'cairo', kind: 'Vesting' },
|
|
48
|
+
'cairo-custom': { template: 'cairo', kind: 'Custom' },
|
|
49
|
+
'stellar-fungible': { template: 'stellar', kind: 'Fungible' },
|
|
50
|
+
'stellar-non-fungible': { template: 'stellar', kind: 'NonFungible' },
|
|
51
|
+
'stellar-stablecoin': { template: 'stellar', kind: 'Stablecoin' },
|
|
52
|
+
'stellar-governor': { template: 'stellar', kind: 'Governor' },
|
|
53
|
+
'stellar-vault': { template: 'stellar', kind: 'Vault' },
|
|
54
|
+
'stylus-erc20': { template: 'stylus', kind: 'ERC20' },
|
|
55
|
+
'stylus-erc721': { template: 'stylus', kind: 'ERC721' },
|
|
56
|
+
'stylus-erc1155': { template: 'stylus', kind: 'ERC1155' },
|
|
57
|
+
'confidential-erc7984': { template: 'confidential', kind: 'ERC7984' },
|
|
58
|
+
'uniswap-hooks': { template: 'uniswap-hooks', kind: 'Hooks' },
|
|
59
|
+
};
|
|
19
60
|
function appResourceUri(toolName) {
|
|
20
61
|
return `ui://openzeppelin/${toolName}.html`;
|
|
21
62
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
63
|
+
function getToolAppSpec(toolName) {
|
|
64
|
+
const spec = exports.TOOL_APP_SPECS[toolName];
|
|
65
|
+
if (spec === undefined) {
|
|
66
|
+
throw new Error(`MCP App mapping missing for ${toolName}. ` +
|
|
67
|
+
`Add it to TOOL_APP_SPECS in packages/mcp/src/apps/register.ts, then run: ` +
|
|
68
|
+
`yarn --cwd packages/mcp build:apps`);
|
|
27
69
|
}
|
|
28
|
-
|
|
70
|
+
return spec;
|
|
71
|
+
}
|
|
72
|
+
/** Resolve language HTML template path, throwing build guidance when missing. */
|
|
73
|
+
function resolveAppHtmlPath(template, toolName) {
|
|
74
|
+
const templatePath = path_1.default.join(APPS_DIR, `${template}.html`);
|
|
75
|
+
if (fs_1.default.existsSync(templatePath)) {
|
|
76
|
+
return templatePath;
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`MCP App HTML missing for ${toolName} (looked for template ${template}.html in ${APPS_DIR}). ` +
|
|
29
79
|
`Run: yarn --cwd packages/mcp build:apps ` +
|
|
30
80
|
`(npm consumers: reinstall the package or report a packaging bug).`);
|
|
31
81
|
}
|
|
82
|
+
function injectKind(templateHtml, kind, toolName) {
|
|
83
|
+
// Replace the bare placeholder so either "__OZ_MCP_KIND__" or '__OZ_MCP_KIND__' becomes the kind.
|
|
84
|
+
const matches = templateHtml.split(exports.MCP_KIND_PLACEHOLDER).length - 1;
|
|
85
|
+
if (matches !== 1) {
|
|
86
|
+
throw new Error(`MCP App template for ${toolName} must contain exactly one ${exports.MCP_KIND_PLACEHOLDER} ` +
|
|
87
|
+
`kind placeholder (found ${matches}). Rebuild with yarn --cwd packages/mcp build:apps.`);
|
|
88
|
+
}
|
|
89
|
+
const html = templateHtml.split(exports.MCP_KIND_PLACEHOLDER).join(kind);
|
|
90
|
+
if (html.includes(exports.MCP_KIND_PLACEHOLDER)) {
|
|
91
|
+
throw new Error(`MCP App kind injection left a kind placeholder in HTML for ${toolName}`);
|
|
92
|
+
}
|
|
93
|
+
if (!html.includes(kind)) {
|
|
94
|
+
throw new Error(`MCP App kind injection failed to embed kind ${kind} for ${toolName}`);
|
|
95
|
+
}
|
|
96
|
+
return html;
|
|
97
|
+
}
|
|
32
98
|
/**
|
|
33
99
|
* App HTML is an immutable build artifact, so it is read from disk at most once per tool and then
|
|
34
100
|
* served from memory. Keyed by tool name rather than per request or per session, so the hosted
|
|
35
101
|
* server holds one copy no matter how many sessions open the same app; memory is bounded by the
|
|
36
102
|
* number of tools, not by traffic.
|
|
37
103
|
*
|
|
104
|
+
* Language templates are shared on disk; kind is injected once per tool on first read.
|
|
105
|
+
*
|
|
38
106
|
* The in-flight promise is cached so concurrent first reads share a single disk read, and a failed
|
|
39
107
|
* read is evicted so a transient error cannot poison a tool for the process lifetime.
|
|
40
108
|
*
|
|
@@ -43,12 +111,31 @@ function resolveAppHtmlPath(toolName) {
|
|
|
43
111
|
* refactor. See that test for why the hosted server depends on them.
|
|
44
112
|
*/
|
|
45
113
|
const htmlCache = new Map();
|
|
114
|
+
const templateCache = new Map();
|
|
115
|
+
function readTemplateHtml(template, toolName) {
|
|
116
|
+
const cached = templateCache.get(template);
|
|
117
|
+
if (cached !== undefined) {
|
|
118
|
+
return cached;
|
|
119
|
+
}
|
|
120
|
+
const pending = fs_1.default.promises.readFile(resolveAppHtmlPath(template, toolName), 'utf-8').catch((e) => {
|
|
121
|
+
templateCache.delete(template);
|
|
122
|
+
throw e;
|
|
123
|
+
});
|
|
124
|
+
templateCache.set(template, pending);
|
|
125
|
+
return pending;
|
|
126
|
+
}
|
|
46
127
|
function readAppHtml(toolName) {
|
|
47
128
|
const cached = htmlCache.get(toolName);
|
|
48
129
|
if (cached !== undefined) {
|
|
49
130
|
return cached;
|
|
50
131
|
}
|
|
51
|
-
|
|
132
|
+
// Resolve mapping synchronously so unknown tools fail before advertising UI.
|
|
133
|
+
const spec = getToolAppSpec(toolName);
|
|
134
|
+
// Fail closed on missing template before caching a doomed read.
|
|
135
|
+
resolveAppHtmlPath(spec.template, toolName);
|
|
136
|
+
const pending = readTemplateHtml(spec.template, toolName)
|
|
137
|
+
.then(templateHtml => injectKind(templateHtml, spec.kind, toolName))
|
|
138
|
+
.catch((e) => {
|
|
52
139
|
htmlCache.delete(toolName);
|
|
53
140
|
throw e;
|
|
54
141
|
});
|
|
@@ -59,7 +146,8 @@ function readAppHtml(toolName) {
|
|
|
59
146
|
function registerWizardAppResource(server, toolName, options) {
|
|
60
147
|
const uri = appResourceUri(toolName);
|
|
61
148
|
// Fail closed: do not register a UI resource that cannot be served.
|
|
62
|
-
|
|
149
|
+
const spec = getToolAppSpec(toolName);
|
|
150
|
+
resolveAppHtmlPath(spec.template, toolName);
|
|
63
151
|
server.registerResource(options?.title ?? `${toolName} UI`, uri, {
|
|
64
152
|
mimeType: exports.RESOURCE_MIME_TYPE,
|
|
65
153
|
description: `Interactive Wizard UI for ${toolName}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/apps/register.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/apps/register.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openzeppelin/contracts-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "OpenZeppelin Contracts MCP Server",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": "https://github.com/OpenZeppelin/contracts-wizard",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
30
|
-
"@openzeppelin/wizard-common": "^0.5.
|
|
31
|
-
"@openzeppelin/wizard": "^0.10.
|
|
30
|
+
"@openzeppelin/wizard-common": "^0.5.5",
|
|
31
|
+
"@openzeppelin/wizard": "^0.10.13",
|
|
32
32
|
"@openzeppelin/wizard-stylus": "^0.3.0",
|
|
33
33
|
"@openzeppelin/wizard-stellar": "^0.6.3",
|
|
34
34
|
"@openzeppelin/wizard-cairo": "^3.0.0",
|
package/src/apps/register.ts
CHANGED
|
@@ -11,29 +11,115 @@ const RESOURCE_URI_META_KEY = 'ui/resourceUri';
|
|
|
11
11
|
|
|
12
12
|
const APPS_DIR = path.join(__dirname, '..', '..', 'apps');
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Kind placeholder baked into language HTML templates by the UI build.
|
|
16
|
+
* Duplicated in packages/ui (kind-placeholder.ts, package-mcp-apps.mjs); kept aligned by
|
|
17
|
+
* `kind placeholder stays in sync` in register.test.ts.
|
|
18
|
+
*/
|
|
19
|
+
export const MCP_KIND_PLACEHOLDER = '__OZ_MCP_KIND__';
|
|
20
|
+
export type AppTemplate = 'solidity' | 'cairo' | 'stellar' | 'stylus' | 'confidential' | 'uniswap-hooks';
|
|
21
|
+
|
|
22
|
+
export type ToolAppSpec = {
|
|
23
|
+
template: AppTemplate;
|
|
24
|
+
kind: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Exhaustive tool → language template + kind map.
|
|
29
|
+
* Kept in lockstep with packages/core kinds by `TOOL_APP_SPECS matches every core kind and UI language entry`
|
|
30
|
+
* in server.test.ts — do not add tools here without that test (and the UI entries/<language>.ts) passing.
|
|
31
|
+
* Overrides match MCP_TOOL_NAME_OVERRIDES in server.test.ts: solidity-rwa ← RealWorldAsset, uniswap-hooks ← Hooks.
|
|
32
|
+
*/
|
|
33
|
+
export const TOOL_APP_SPECS: Readonly<Record<string, ToolAppSpec>> = {
|
|
34
|
+
'solidity-erc20': { template: 'solidity', kind: 'ERC20' },
|
|
35
|
+
'solidity-erc721': { template: 'solidity', kind: 'ERC721' },
|
|
36
|
+
'solidity-erc1155': { template: 'solidity', kind: 'ERC1155' },
|
|
37
|
+
'solidity-stablecoin': { template: 'solidity', kind: 'Stablecoin' },
|
|
38
|
+
'solidity-rwa': { template: 'solidity', kind: 'RealWorldAsset' },
|
|
39
|
+
'solidity-account': { template: 'solidity', kind: 'Account' },
|
|
40
|
+
'solidity-governor': { template: 'solidity', kind: 'Governor' },
|
|
41
|
+
'solidity-custom': { template: 'solidity', kind: 'Custom' },
|
|
42
|
+
|
|
43
|
+
'cairo-erc20': { template: 'cairo', kind: 'ERC20' },
|
|
44
|
+
'cairo-erc721': { template: 'cairo', kind: 'ERC721' },
|
|
45
|
+
'cairo-erc1155': { template: 'cairo', kind: 'ERC1155' },
|
|
46
|
+
'cairo-account': { template: 'cairo', kind: 'Account' },
|
|
47
|
+
'cairo-multisig': { template: 'cairo', kind: 'Multisig' },
|
|
48
|
+
'cairo-governor': { template: 'cairo', kind: 'Governor' },
|
|
49
|
+
'cairo-vesting': { template: 'cairo', kind: 'Vesting' },
|
|
50
|
+
'cairo-custom': { template: 'cairo', kind: 'Custom' },
|
|
51
|
+
|
|
52
|
+
'stellar-fungible': { template: 'stellar', kind: 'Fungible' },
|
|
53
|
+
'stellar-non-fungible': { template: 'stellar', kind: 'NonFungible' },
|
|
54
|
+
'stellar-stablecoin': { template: 'stellar', kind: 'Stablecoin' },
|
|
55
|
+
'stellar-governor': { template: 'stellar', kind: 'Governor' },
|
|
56
|
+
'stellar-vault': { template: 'stellar', kind: 'Vault' },
|
|
57
|
+
|
|
58
|
+
'stylus-erc20': { template: 'stylus', kind: 'ERC20' },
|
|
59
|
+
'stylus-erc721': { template: 'stylus', kind: 'ERC721' },
|
|
60
|
+
'stylus-erc1155': { template: 'stylus', kind: 'ERC1155' },
|
|
61
|
+
|
|
62
|
+
'confidential-erc7984': { template: 'confidential', kind: 'ERC7984' },
|
|
63
|
+
|
|
64
|
+
'uniswap-hooks': { template: 'uniswap-hooks', kind: 'Hooks' },
|
|
65
|
+
};
|
|
66
|
+
|
|
14
67
|
export function appResourceUri(toolName: string): string {
|
|
15
68
|
return `ui://openzeppelin/${toolName}.html`;
|
|
16
69
|
}
|
|
17
70
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
71
|
+
export function getToolAppSpec(toolName: string): ToolAppSpec {
|
|
72
|
+
const spec = TOOL_APP_SPECS[toolName];
|
|
73
|
+
if (spec === undefined) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`MCP App mapping missing for ${toolName}. ` +
|
|
76
|
+
`Add it to TOOL_APP_SPECS in packages/mcp/src/apps/register.ts, then run: ` +
|
|
77
|
+
`yarn --cwd packages/mcp build:apps`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return spec;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Resolve language HTML template path, throwing build guidance when missing. */
|
|
84
|
+
function resolveAppHtmlPath(template: AppTemplate, toolName: string): string {
|
|
85
|
+
const templatePath = path.join(APPS_DIR, `${template}.html`);
|
|
86
|
+
if (fs.existsSync(templatePath)) {
|
|
87
|
+
return templatePath;
|
|
23
88
|
}
|
|
24
89
|
throw new Error(
|
|
25
|
-
`MCP App HTML missing for ${toolName} (looked in ${APPS_DIR}). ` +
|
|
90
|
+
`MCP App HTML missing for ${toolName} (looked for template ${template}.html in ${APPS_DIR}). ` +
|
|
26
91
|
`Run: yarn --cwd packages/mcp build:apps ` +
|
|
27
92
|
`(npm consumers: reinstall the package or report a packaging bug).`,
|
|
28
93
|
);
|
|
29
94
|
}
|
|
30
95
|
|
|
96
|
+
function injectKind(templateHtml: string, kind: string, toolName: string): string {
|
|
97
|
+
// Replace the bare placeholder so either "__OZ_MCP_KIND__" or '__OZ_MCP_KIND__' becomes the kind.
|
|
98
|
+
const matches = templateHtml.split(MCP_KIND_PLACEHOLDER).length - 1;
|
|
99
|
+
if (matches !== 1) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`MCP App template for ${toolName} must contain exactly one ${MCP_KIND_PLACEHOLDER} ` +
|
|
102
|
+
`kind placeholder (found ${matches}). Rebuild with yarn --cwd packages/mcp build:apps.`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const html = templateHtml.split(MCP_KIND_PLACEHOLDER).join(kind);
|
|
106
|
+
if (html.includes(MCP_KIND_PLACEHOLDER)) {
|
|
107
|
+
throw new Error(`MCP App kind injection left a kind placeholder in HTML for ${toolName}`);
|
|
108
|
+
}
|
|
109
|
+
if (!html.includes(kind)) {
|
|
110
|
+
throw new Error(`MCP App kind injection failed to embed kind ${kind} for ${toolName}`);
|
|
111
|
+
}
|
|
112
|
+
return html;
|
|
113
|
+
}
|
|
114
|
+
|
|
31
115
|
/**
|
|
32
116
|
* App HTML is an immutable build artifact, so it is read from disk at most once per tool and then
|
|
33
117
|
* served from memory. Keyed by tool name rather than per request or per session, so the hosted
|
|
34
118
|
* server holds one copy no matter how many sessions open the same app; memory is bounded by the
|
|
35
119
|
* number of tools, not by traffic.
|
|
36
120
|
*
|
|
121
|
+
* Language templates are shared on disk; kind is injected once per tool on first read.
|
|
122
|
+
*
|
|
37
123
|
* The in-flight promise is cached so concurrent first reads share a single disk read, and a failed
|
|
38
124
|
* read is evicted so a transient error cannot poison a tool for the process lifetime.
|
|
39
125
|
*
|
|
@@ -42,16 +128,37 @@ function resolveAppHtmlPath(toolName: string): string {
|
|
|
42
128
|
* refactor. See that test for why the hosted server depends on them.
|
|
43
129
|
*/
|
|
44
130
|
const htmlCache = new Map<string, Promise<string>>();
|
|
131
|
+
const templateCache = new Map<AppTemplate, Promise<string>>();
|
|
45
132
|
|
|
46
|
-
|
|
47
|
-
const cached =
|
|
133
|
+
function readTemplateHtml(template: AppTemplate, toolName: string): Promise<string> {
|
|
134
|
+
const cached = templateCache.get(template);
|
|
48
135
|
if (cached !== undefined) {
|
|
49
136
|
return cached;
|
|
50
137
|
}
|
|
51
|
-
const pending = fs.promises.readFile(resolveAppHtmlPath(toolName), 'utf-8').catch((e: unknown) => {
|
|
52
|
-
|
|
138
|
+
const pending = fs.promises.readFile(resolveAppHtmlPath(template, toolName), 'utf-8').catch((e: unknown) => {
|
|
139
|
+
templateCache.delete(template);
|
|
53
140
|
throw e;
|
|
54
141
|
});
|
|
142
|
+
templateCache.set(template, pending);
|
|
143
|
+
return pending;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function readAppHtml(toolName: string): Promise<string> {
|
|
147
|
+
const cached = htmlCache.get(toolName);
|
|
148
|
+
if (cached !== undefined) {
|
|
149
|
+
return cached;
|
|
150
|
+
}
|
|
151
|
+
// Resolve mapping synchronously so unknown tools fail before advertising UI.
|
|
152
|
+
const spec = getToolAppSpec(toolName);
|
|
153
|
+
// Fail closed on missing template before caching a doomed read.
|
|
154
|
+
resolveAppHtmlPath(spec.template, toolName);
|
|
155
|
+
|
|
156
|
+
const pending = readTemplateHtml(spec.template, toolName)
|
|
157
|
+
.then(templateHtml => injectKind(templateHtml, spec.kind, toolName))
|
|
158
|
+
.catch((e: unknown) => {
|
|
159
|
+
htmlCache.delete(toolName);
|
|
160
|
+
throw e;
|
|
161
|
+
});
|
|
55
162
|
htmlCache.set(toolName, pending);
|
|
56
163
|
return pending;
|
|
57
164
|
}
|
|
@@ -60,7 +167,8 @@ export function readAppHtml(toolName: string): Promise<string> {
|
|
|
60
167
|
function registerWizardAppResource(server: McpServer, toolName: string, options?: { title?: string }): void {
|
|
61
168
|
const uri = appResourceUri(toolName);
|
|
62
169
|
// Fail closed: do not register a UI resource that cannot be served.
|
|
63
|
-
|
|
170
|
+
const spec = getToolAppSpec(toolName);
|
|
171
|
+
resolveAppHtmlPath(spec.template, toolName);
|
|
64
172
|
server.registerResource(
|
|
65
173
|
options?.title ?? `${toolName} UI`,
|
|
66
174
|
uri,
|