@oobe-protocol-labs/sap-mcp-server 0.7.3 → 0.8.0
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/CHANGELOG.md +139 -0
- package/README.md +14 -7
- package/USER_DOCS/00_START_HERE.md +45 -7
- package/USER_DOCS/01_HOSTED_REMOTE_MCP.md +2 -0
- package/USER_DOCS/03_PAYMENTS_X402_PAYSH.md +13 -5
- package/USER_DOCS/04_CLIENT_CONFIGS.md +25 -15
- package/USER_DOCS/06_DESKTOP_GUI_WIZARD.md +36 -21
- package/assets/logos/drift.svg +12 -0
- package/assets/logos/jupiter.ico +0 -0
- package/assets/logos/mcp.svg +11 -0
- package/assets/logos/meteora.png +0 -0
- package/assets/logos/orca.ico +0 -0
- package/assets/logos/raydium.ico +0 -0
- package/assets/logos/smithery.svg +11 -0
- package/assets/oobe-logo.png +0 -0
- package/dist/config/mcp-client-injection.d.ts +26 -7
- package/dist/config/mcp-client-injection.d.ts.map +1 -1
- package/dist/config/mcp-client-injection.js +182 -58
- package/dist/config/mcp-client-injection.js.map +1 -1
- package/dist/config/wizard.d.ts.map +1 -1
- package/dist/config/wizard.js +92 -57
- package/dist/config/wizard.js.map +1 -1
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/payments/pricing.d.ts.map +1 -1
- package/dist/payments/pricing.js +11 -0
- package/dist/payments/pricing.js.map +1 -1
- package/dist/payments/x402-paid-call.d.ts +85 -1
- package/dist/payments/x402-paid-call.d.ts.map +1 -1
- package/dist/payments/x402-paid-call.js +168 -4
- package/dist/payments/x402-paid-call.js.map +1 -1
- package/dist/prompts/context/sap-agent-context.prompt.js +6 -3
- package/dist/prompts/context/sap-agent-context.prompt.js.map +1 -1
- package/dist/remote/public-home/escape.d.ts +11 -0
- package/dist/remote/public-home/escape.d.ts.map +1 -0
- package/dist/remote/public-home/escape.js +20 -0
- package/dist/remote/public-home/escape.js.map +1 -0
- package/dist/remote/public-home/index.d.ts +8 -0
- package/dist/remote/public-home/index.d.ts.map +1 -0
- package/dist/remote/public-home/index.js +57 -0
- package/dist/remote/public-home/index.js.map +1 -0
- package/dist/remote/public-home/scripts.d.ts +6 -0
- package/dist/remote/public-home/scripts.d.ts.map +1 -0
- package/dist/remote/public-home/scripts.js +106 -0
- package/dist/remote/public-home/scripts.js.map +1 -0
- package/dist/remote/public-home/sections.d.ts +67 -0
- package/dist/remote/public-home/sections.d.ts.map +1 -0
- package/dist/remote/public-home/sections.js +727 -0
- package/dist/remote/public-home/sections.js.map +1 -0
- package/dist/remote/public-home/styles.d.ts +6 -0
- package/dist/remote/public-home/styles.d.ts.map +1 -0
- package/dist/remote/public-home/styles.js +1534 -0
- package/dist/remote/public-home/styles.js.map +1 -0
- package/dist/remote/public-home/types.d.ts +95 -0
- package/dist/remote/public-home/types.d.ts.map +1 -0
- package/dist/remote/public-home/types.js +2 -0
- package/dist/remote/public-home/types.js.map +1 -0
- package/dist/remote/server.d.ts +41 -1
- package/dist/remote/server.d.ts.map +1 -1
- package/dist/remote/server.js +109 -292
- package/dist/remote/server.js.map +1 -1
- package/dist/security/tool-permissions.d.ts.map +1 -1
- package/dist/security/tool-permissions.js +5 -0
- package/dist/security/tool-permissions.js.map +1 -1
- package/dist/tools/magicblock-tools.js +6 -6
- package/dist/tools/magicblock-tools.js.map +1 -1
- package/dist/tools/profile-tools.d.ts.map +1 -1
- package/dist/tools/profile-tools.js +136 -33
- package/dist/tools/profile-tools.js.map +1 -1
- package/dist/tools/register-tools.d.ts.map +1 -1
- package/dist/tools/register-tools.js +5 -0
- package/dist/tools/register-tools.js.map +1 -1
- package/dist/tools/x402-paid-call-tool.d.ts +1 -1
- package/dist/tools/x402-paid-call-tool.d.ts.map +1 -1
- package/dist/tools/x402-paid-call-tool.js +314 -28
- package/dist/tools/x402-paid-call-tool.js.map +1 -1
- package/dist/wizard-core/desktop-flow.d.ts +40 -1
- package/dist/wizard-core/desktop-flow.d.ts.map +1 -1
- package/dist/wizard-core/desktop-flow.js +206 -15
- package/dist/wizard-core/desktop-flow.js.map +1 -1
- package/docs/03_CONFIGURATION_AND_WIZARD.md +6 -2
- package/docs/06_PAYMENTS_X402_AND_PAYSH.md +29 -7
- package/docs/07_ENDPOINTS_AND_CLIENTS.md +1 -1
- package/docs/14_DESKTOP_WIZARD_RELEASE.md +24 -5
- package/docs/15_DASHBOARD_SCREENSHARE_SCRIPT.md +137 -0
- package/docs/README.md +23 -2
- package/package.json +1 -1
- package/server.json +3 -3
- package/skills/sap-mcp/SKILL.md +23 -7
- package/skills/sap-payments-x402/SKILL.md +37 -13
package/dist/remote/server.js
CHANGED
|
@@ -17,9 +17,12 @@ import { AuthManager } from './auth/index.js';
|
|
|
17
17
|
import { McpMonetizationGate, resolvePaymentNetwork } from '../payments/index.js';
|
|
18
18
|
import { generatePayShProviderYaml } from '../payments/pay-sh-spec.js';
|
|
19
19
|
import { RemoteRateLimiter, buildRemoteRateLimitConfigFromEnv } from './rate-limiter.js';
|
|
20
|
+
import { renderLandingPage } from './public-home/index.js';
|
|
20
21
|
const PUBLIC_SERVER_TITLE = 'SAP MCP Server | OOBE Protocol';
|
|
21
22
|
const PUBLIC_SERVER_DESCRIPTION = 'Hosted Solana-native MCP gateway for Synapse Agent Protocol tools, x402/pay.sh monetization, SNS identity, and agent operations.';
|
|
22
23
|
const LOGO_ASSET_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'assets', 'explorer_logo.png');
|
|
24
|
+
const OOBE_LOGO_ASSET_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'assets', 'oobe-logo.png');
|
|
25
|
+
const LOGO_ASSET_ROOT_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'assets', 'logos');
|
|
23
26
|
const DOCS_ROOT_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'docs');
|
|
24
27
|
const USER_DOCS_ROOT_PATH = join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'USER_DOCS');
|
|
25
28
|
const PAYMENT_STATS_CACHE_MS = 15_000;
|
|
@@ -33,8 +36,18 @@ const USDC_MAINNET_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
|
33
36
|
const USDC_DEVNET_MINT = '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU';
|
|
34
37
|
const MCP_REGISTRY_AUTH_PATH = '/.well-known/mcp-registry-auth';
|
|
35
38
|
let logoAssetCache;
|
|
39
|
+
let oobeLogoAssetCache;
|
|
36
40
|
let paymentStatsCache;
|
|
37
41
|
let serverCardCapabilitiesCache;
|
|
42
|
+
const PUBLIC_LOGO_ASSETS = {
|
|
43
|
+
'/logos/drift.svg': { filename: 'drift.svg', contentType: 'image/svg+xml' },
|
|
44
|
+
'/logos/jupiter.ico': { filename: 'jupiter.ico', contentType: 'image/x-icon' },
|
|
45
|
+
'/logos/mcp.svg': { filename: 'mcp.svg', contentType: 'image/svg+xml' },
|
|
46
|
+
'/logos/meteora.png': { filename: 'meteora.png', contentType: 'image/png' },
|
|
47
|
+
'/logos/orca.ico': { filename: 'orca.ico', contentType: 'image/x-icon' },
|
|
48
|
+
'/logos/raydium.ico': { filename: 'raydium.ico', contentType: 'image/x-icon' },
|
|
49
|
+
'/logos/smithery.svg': { filename: 'smithery.svg', contentType: 'image/svg+xml' },
|
|
50
|
+
};
|
|
38
51
|
/**
|
|
39
52
|
* @name parseApiKeys
|
|
40
53
|
* @description Parses comma-separated `apiKey=userId` entries from environment configuration.
|
|
@@ -376,6 +389,23 @@ export function resolvePublicLogoAsset(method, pathname) {
|
|
|
376
389
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
377
390
|
return undefined;
|
|
378
391
|
}
|
|
392
|
+
const logoAsset = PUBLIC_LOGO_ASSETS[pathname];
|
|
393
|
+
if (logoAsset !== undefined) {
|
|
394
|
+
const image = readFileSync(join(LOGO_ASSET_ROOT_PATH, logoAsset.filename));
|
|
395
|
+
return {
|
|
396
|
+
contentType: logoAsset.contentType,
|
|
397
|
+
contentLength: image.byteLength,
|
|
398
|
+
body: method === 'HEAD' ? undefined : image,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (pathname === '/oobe-logo.png') {
|
|
402
|
+
const image = readOobeLogoAsset();
|
|
403
|
+
return {
|
|
404
|
+
contentType: 'image/png',
|
|
405
|
+
contentLength: image.byteLength,
|
|
406
|
+
body: method === 'HEAD' ? undefined : image,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
379
409
|
const image = readLogoAsset();
|
|
380
410
|
const body = method === 'HEAD' ? undefined : image;
|
|
381
411
|
if (['/favicon.png', '/apple-touch-icon.png', '/og.png'].includes(pathname)) {
|
|
@@ -415,6 +445,14 @@ function readLogoAsset() {
|
|
|
415
445
|
logoAssetCache ??= readFileSync(LOGO_ASSET_PATH);
|
|
416
446
|
return logoAssetCache;
|
|
417
447
|
}
|
|
448
|
+
/**
|
|
449
|
+
* @name readOobeLogoAsset
|
|
450
|
+
* @description Loads the bundled OOBE mark used by the public navigation link.
|
|
451
|
+
*/
|
|
452
|
+
function readOobeLogoAsset() {
|
|
453
|
+
oobeLogoAssetCache ??= readFileSync(OOBE_LOGO_ASSET_PATH);
|
|
454
|
+
return oobeLogoAssetCache;
|
|
455
|
+
}
|
|
418
456
|
/**
|
|
419
457
|
* @name readPublicPaymentStats
|
|
420
458
|
* @description Reads cached public-safe payment totals from the append-only usage ledger.
|
|
@@ -530,11 +568,31 @@ function buildPublicBaseUrl(req, config) {
|
|
|
530
568
|
export function buildPublicServerInfo(req, config) {
|
|
531
569
|
const baseUrl = buildPublicBaseUrl(req, config);
|
|
532
570
|
const authRequired = config.auth.type !== 'none';
|
|
571
|
+
const homepage = `${baseUrl}/`;
|
|
572
|
+
const iconUrl = `${baseUrl}/favicon.png`;
|
|
573
|
+
const icons = [
|
|
574
|
+
{
|
|
575
|
+
src: iconUrl,
|
|
576
|
+
mimeType: 'image/png',
|
|
577
|
+
sizes: ['512x512'],
|
|
578
|
+
},
|
|
579
|
+
];
|
|
533
580
|
return {
|
|
534
581
|
name: 'sap-mcp-server',
|
|
535
582
|
title: PUBLIC_SERVER_TITLE,
|
|
583
|
+
displayName: PUBLIC_SERVER_TITLE,
|
|
584
|
+
display_name: PUBLIC_SERVER_TITLE,
|
|
536
585
|
description: PUBLIC_SERVER_DESCRIPTION,
|
|
537
586
|
version: MCP_SERVER_VERSION,
|
|
587
|
+
homepage,
|
|
588
|
+
websiteUrl: homepage,
|
|
589
|
+
website_url: homepage,
|
|
590
|
+
icon: iconUrl,
|
|
591
|
+
iconUrl,
|
|
592
|
+
icon_url: iconUrl,
|
|
593
|
+
image: iconUrl,
|
|
594
|
+
logo: iconUrl,
|
|
595
|
+
icons,
|
|
538
596
|
status: 'online',
|
|
539
597
|
protocol: {
|
|
540
598
|
primary: 'mcp',
|
|
@@ -623,23 +681,47 @@ export async function buildStaticServerCard(req, config, appConfig) {
|
|
|
623
681
|
const baseUrl = buildPublicBaseUrl(req, config);
|
|
624
682
|
const capabilities = await getStaticServerCardCapabilities(appConfig);
|
|
625
683
|
const authRequired = config.auth.type !== 'none';
|
|
684
|
+
const homepage = `${baseUrl}/`;
|
|
685
|
+
const iconUrl = `${baseUrl}/favicon.png`;
|
|
686
|
+
const icons = [
|
|
687
|
+
{
|
|
688
|
+
src: iconUrl,
|
|
689
|
+
mimeType: 'image/png',
|
|
690
|
+
sizes: ['512x512'],
|
|
691
|
+
},
|
|
692
|
+
];
|
|
626
693
|
return {
|
|
694
|
+
name: 'sap-mcp-server',
|
|
695
|
+
displayName: PUBLIC_SERVER_TITLE,
|
|
696
|
+
display_name: PUBLIC_SERVER_TITLE,
|
|
697
|
+
title: PUBLIC_SERVER_TITLE,
|
|
698
|
+
version: MCP_SERVER_VERSION,
|
|
699
|
+
description: PUBLIC_SERVER_DESCRIPTION,
|
|
700
|
+
homepage,
|
|
701
|
+
websiteUrl: homepage,
|
|
702
|
+
website_url: homepage,
|
|
703
|
+
icon: iconUrl,
|
|
704
|
+
iconUrl,
|
|
705
|
+
icon_url: iconUrl,
|
|
706
|
+
image: iconUrl,
|
|
707
|
+
logo: iconUrl,
|
|
708
|
+
icons,
|
|
627
709
|
serverInfo: {
|
|
628
710
|
name: 'sap-mcp-server',
|
|
629
711
|
displayName: PUBLIC_SERVER_TITLE,
|
|
712
|
+
display_name: PUBLIC_SERVER_TITLE,
|
|
630
713
|
title: PUBLIC_SERVER_TITLE,
|
|
631
714
|
version: MCP_SERVER_VERSION,
|
|
632
715
|
description: PUBLIC_SERVER_DESCRIPTION,
|
|
633
|
-
homepage
|
|
634
|
-
websiteUrl:
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
],
|
|
716
|
+
homepage,
|
|
717
|
+
websiteUrl: homepage,
|
|
718
|
+
website_url: homepage,
|
|
719
|
+
icon: iconUrl,
|
|
720
|
+
iconUrl,
|
|
721
|
+
icon_url: iconUrl,
|
|
722
|
+
image: iconUrl,
|
|
723
|
+
logo: iconUrl,
|
|
724
|
+
icons,
|
|
643
725
|
},
|
|
644
726
|
authentication: {
|
|
645
727
|
required: authRequired,
|
|
@@ -684,7 +766,7 @@ export function buildOpenApiSpec(req, config) {
|
|
|
684
766
|
title: 'SAP MCP Server',
|
|
685
767
|
version: MCP_SERVER_VERSION,
|
|
686
768
|
description: 'MCP gateway for Solana DeFi, SAP agent registry, and x402 micropayments. 248 tools across Jupiter, Drift, Metaplex, Pump.fun, Raydium, Orca, and more.',
|
|
687
|
-
'x-guidance': 'Connect to POST /mcp with Accept: application/json, text/event-stream. Initialize an MCP session first, then call tools. Paid tools return a payment_required JSON-RPC error (HTTP 200 for MCP SDK compatibility) or HTTP 402 for non-MCP clients. Use
|
|
769
|
+
'x-guidance': 'Connect to POST /mcp with Accept: application/json, text/event-stream. Initialize an MCP session first, then call tools. Paid tools return a payment_required JSON-RPC error (HTTP 200 for MCP SDK compatibility) or HTTP 402 for non-MCP clients. Use the local sap_payments_call_paid_tool bridge or npx sap-mcp-x402-paid-call to self-pay and retry.',
|
|
688
770
|
contact: {
|
|
689
771
|
name: 'OOBE Protocol Labs',
|
|
690
772
|
url: 'https://github.com/OOBE-PROTOCOL/sap-mcp',
|
|
@@ -755,7 +837,11 @@ export function buildOpenApiSpec(req, config) {
|
|
|
755
837
|
description: 'MCP method.',
|
|
756
838
|
examples: ['initialize', 'tools/list', 'prompts/list', 'resources/list', 'ping'],
|
|
757
839
|
},
|
|
758
|
-
params: {
|
|
840
|
+
params: {
|
|
841
|
+
type: 'object',
|
|
842
|
+
description: 'JSON-RPC parameters for the requested MCP method. Use an empty object when the method has no parameters.',
|
|
843
|
+
additionalProperties: true,
|
|
844
|
+
},
|
|
759
845
|
},
|
|
760
846
|
required: ['jsonrpc', 'id', 'method'],
|
|
761
847
|
},
|
|
@@ -894,262 +980,17 @@ export function buildLandingHtml(req, config, endpoint = 'root') {
|
|
|
894
980
|
const title = endpoint === 'mcp'
|
|
895
981
|
? 'SAP MCP Streamable HTTP Endpoint | OOBE Protocol'
|
|
896
982
|
: info.title;
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
<meta name="description" content="${escapeHtml(info.description)}">
|
|
909
|
-
<meta name="robots" content="index,follow">
|
|
910
|
-
<link rel="canonical" href="${escapeHtml(pageUrl)}">
|
|
911
|
-
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
|
912
|
-
<link rel="icon" type="image/png" href="/favicon.png">
|
|
913
|
-
<link rel="shortcut icon" href="/favicon.ico">
|
|
914
|
-
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
915
|
-
<meta property="og:type" content="website">
|
|
916
|
-
<meta property="og:title" content="${escapeHtml(title)}">
|
|
917
|
-
<meta property="og:description" content="${escapeHtml(info.description)}">
|
|
918
|
-
<meta property="og:url" content="${escapeHtml(pageUrl)}">
|
|
919
|
-
<meta property="og:image" content="${escapeHtml(info.endpoints.favicon)}">
|
|
920
|
-
<meta name="twitter:card" content="summary">
|
|
921
|
-
<meta name="twitter:title" content="${escapeHtml(title)}">
|
|
922
|
-
<meta name="twitter:description" content="${escapeHtml(info.description)}">
|
|
923
|
-
<meta name="twitter:image" content="${escapeHtml(info.endpoints.favicon)}">
|
|
924
|
-
<script type="application/ld+json">${publicInfo}</script>
|
|
925
|
-
<style>
|
|
926
|
-
:root {
|
|
927
|
-
color-scheme: dark;
|
|
928
|
-
--aqua: #28d8e8;
|
|
929
|
-
--aqua-soft: rgba(40,216,232,.13);
|
|
930
|
-
--ink: #f4fbff;
|
|
931
|
-
--muted: #a8bdc6;
|
|
932
|
-
--subtle: #76909a;
|
|
933
|
-
--panel: rgba(255,255,255,.062);
|
|
934
|
-
--panel-strong: rgba(255,255,255,.095);
|
|
935
|
-
--line: rgba(255,255,255,.14);
|
|
936
|
-
--line-strong: rgba(40,216,232,.38);
|
|
937
|
-
--surface: #080d12;
|
|
938
|
-
--success: #8ef3c5;
|
|
939
|
-
--warning: #ffd38a;
|
|
940
|
-
}
|
|
941
|
-
* { box-sizing: border-box; }
|
|
942
|
-
html { scroll-behavior: smooth; }
|
|
943
|
-
body {
|
|
944
|
-
margin: 0;
|
|
945
|
-
min-height: 100vh;
|
|
946
|
-
font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
947
|
-
color: var(--ink);
|
|
948
|
-
background:
|
|
949
|
-
linear-gradient(115deg, rgba(40,216,232,.13), transparent 34%),
|
|
950
|
-
linear-gradient(180deg, #0a1218 0%, var(--surface) 46%, #05080b 100%);
|
|
951
|
-
}
|
|
952
|
-
body::before {
|
|
953
|
-
content: "";
|
|
954
|
-
position: fixed;
|
|
955
|
-
inset: 0;
|
|
956
|
-
pointer-events: none;
|
|
957
|
-
background-image: linear-gradient(rgba(255,255,255,.035) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px);
|
|
958
|
-
background-size: 64px 64px;
|
|
959
|
-
mask-image: linear-gradient(to bottom, rgba(0,0,0,.75), transparent 72%);
|
|
960
|
-
}
|
|
961
|
-
main { width: min(1180px, calc(100% - 32px)); margin: 0 auto; padding: 56px 0 40px; position: relative; }
|
|
962
|
-
header { display: grid; grid-template-columns: 92px 1fr; gap: 24px; align-items: center; margin-bottom: 30px; }
|
|
963
|
-
img.logo { width: 92px; height: 92px; border-radius: 24px; box-shadow: 0 18px 70px rgba(40,216,232,.22); }
|
|
964
|
-
h1 { margin: 0; max-width: 920px; font-size: clamp(2.2rem, 5vw, 5rem); line-height: .95; letter-spacing: 0; }
|
|
965
|
-
p { margin: 10px 0 0; color: var(--muted); max-width: 760px; }
|
|
966
|
-
a { color: var(--aqua); text-underline-offset: 4px; }
|
|
967
|
-
a:focus-visible { outline: 2px solid var(--aqua); outline-offset: 3px; border-radius: 6px; }
|
|
968
|
-
code {
|
|
969
|
-
color: #e9fcff;
|
|
970
|
-
overflow-wrap: anywhere;
|
|
971
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
972
|
-
font-size: .92em;
|
|
973
|
-
}
|
|
974
|
-
.badge { display: inline-flex; align-items: center; gap: 8px; color: var(--aqua); font-weight: 750; text-transform: uppercase; font-size: .78rem; letter-spacing: 0; }
|
|
975
|
-
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 22px; }
|
|
976
|
-
.button {
|
|
977
|
-
display: inline-flex;
|
|
978
|
-
min-height: 42px;
|
|
979
|
-
align-items: center;
|
|
980
|
-
justify-content: center;
|
|
981
|
-
border: 1px solid var(--line-strong);
|
|
982
|
-
border-radius: 8px;
|
|
983
|
-
padding: 9px 14px;
|
|
984
|
-
background: var(--aqua-soft);
|
|
985
|
-
color: var(--ink);
|
|
986
|
-
font-weight: 700;
|
|
987
|
-
text-decoration: none;
|
|
988
|
-
}
|
|
989
|
-
.button.secondary { background: transparent; border-color: var(--line); color: var(--aqua); }
|
|
990
|
-
.bento { display: grid; grid-template-columns: repeat(12, 1fr); gap: 14px; margin-top: 28px; }
|
|
991
|
-
.card {
|
|
992
|
-
min-height: 148px;
|
|
993
|
-
border: 1px solid var(--line);
|
|
994
|
-
border-radius: 8px;
|
|
995
|
-
background: var(--panel);
|
|
996
|
-
padding: 18px;
|
|
997
|
-
color: inherit;
|
|
998
|
-
text-decoration: none;
|
|
999
|
-
}
|
|
1000
|
-
.card.strong { background: linear-gradient(180deg, var(--panel-strong), var(--panel)); border-color: var(--line-strong); }
|
|
1001
|
-
.span-3 { grid-column: span 3; }
|
|
1002
|
-
.span-4 { grid-column: span 4; }
|
|
1003
|
-
.span-5 { grid-column: span 5; }
|
|
1004
|
-
.span-6 { grid-column: span 6; }
|
|
1005
|
-
.span-7 { grid-column: span 7; }
|
|
1006
|
-
.span-8 { grid-column: span 8; }
|
|
1007
|
-
.span-12 { grid-column: span 12; }
|
|
1008
|
-
h2 { margin: 0 0 12px; font-size: 1rem; color: var(--aqua); letter-spacing: 0; }
|
|
1009
|
-
h3 { margin: 0 0 6px; font-size: .86rem; color: var(--muted); font-weight: 650; text-transform: uppercase; letter-spacing: 0; }
|
|
1010
|
-
.metric { margin-top: 4px; font-size: clamp(2rem, 4vw, 3.35rem); line-height: 1; font-weight: 800; letter-spacing: 0; }
|
|
1011
|
-
.metric.small { font-size: clamp(1.55rem, 3vw, 2.3rem); }
|
|
1012
|
-
.caption { margin-top: 10px; color: var(--subtle); font-size: .94rem; }
|
|
1013
|
-
.status { color: var(--success); }
|
|
1014
|
-
.warning { color: var(--warning); }
|
|
1015
|
-
.list { padding-left: 18px; margin: 0; color: var(--muted); }
|
|
1016
|
-
.list li + li { margin-top: 8px; }
|
|
1017
|
-
.command { margin-top: 12px; border: 1px solid rgba(255,255,255,.10); border-radius: 8px; background: rgba(0,0,0,.28); padding: 13px; }
|
|
1018
|
-
.download-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 14px; }
|
|
1019
|
-
.download-link {
|
|
1020
|
-
display: grid;
|
|
1021
|
-
min-height: 74px;
|
|
1022
|
-
align-content: center;
|
|
1023
|
-
gap: 4px;
|
|
1024
|
-
border: 1px solid var(--line);
|
|
1025
|
-
border-radius: 8px;
|
|
1026
|
-
background: rgba(0,0,0,.18);
|
|
1027
|
-
padding: 12px;
|
|
1028
|
-
color: var(--ink);
|
|
1029
|
-
text-decoration: none;
|
|
1030
|
-
}
|
|
1031
|
-
.download-link strong { color: var(--aqua); }
|
|
1032
|
-
.download-link span { color: var(--muted); font-size: .88rem; }
|
|
1033
|
-
.endpoint-list { display: grid; gap: 10px; margin-top: 10px; }
|
|
1034
|
-
.endpoint-row { display: grid; grid-template-columns: 72px 1fr; gap: 12px; align-items: baseline; }
|
|
1035
|
-
.method { color: var(--success); font: 700 .78rem/1 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
1036
|
-
.footer { margin-top: 26px; color: var(--muted); font-size: .94rem; }
|
|
1037
|
-
@media (max-width: 860px) {
|
|
1038
|
-
header { grid-template-columns: 1fr; }
|
|
1039
|
-
img.logo { width: 76px; height: 76px; border-radius: 20px; }
|
|
1040
|
-
.span-3, .span-4, .span-5, .span-6, .span-7, .span-8 { grid-column: span 12; }
|
|
1041
|
-
.download-grid { grid-template-columns: 1fr; }
|
|
1042
|
-
}
|
|
1043
|
-
@media (min-width: 861px) and (max-width: 1120px) {
|
|
1044
|
-
.span-3, .span-4 { grid-column: span 6; }
|
|
1045
|
-
.span-5, .span-6, .span-7, .span-8 { grid-column: span 12; }
|
|
1046
|
-
}
|
|
1047
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1048
|
-
html { scroll-behavior: auto; }
|
|
1049
|
-
}
|
|
1050
|
-
</style>
|
|
1051
|
-
</head>
|
|
1052
|
-
<body>
|
|
1053
|
-
<main>
|
|
1054
|
-
<header>
|
|
1055
|
-
<img class="logo" src="/favicon.png" alt="SAP MCP logo" width="92" height="92">
|
|
1056
|
-
<div>
|
|
1057
|
-
<span class="badge">OOBE Protocol • ${escapeHtml(endpointLabel)}</span>
|
|
1058
|
-
<h1>${escapeHtml(title)}</h1>
|
|
1059
|
-
<p>${escapeHtml(info.description)}</p>
|
|
1060
|
-
<div class="hero-actions">
|
|
1061
|
-
<a class="button" href="/wizard/install.sh">Install wizard</a>
|
|
1062
|
-
<a class="button secondary" href="/wizard/downloads.json">Downloads</a>
|
|
1063
|
-
<a class="button secondary" href="/docs">Docs</a>
|
|
1064
|
-
<a class="button secondary" href="/server.json">Server JSON</a>
|
|
1065
|
-
<a class="button secondary" href="/openapi.json">OpenAPI</a>
|
|
1066
|
-
<a class="button secondary" href="/pay/provider.yml">pay.sh YAML</a>
|
|
1067
|
-
<a class="button secondary" href="/.well-known/agent-card.json">Agent card</a>
|
|
1068
|
-
</div>
|
|
1069
|
-
</div>
|
|
1070
|
-
</header>
|
|
1071
|
-
|
|
1072
|
-
<div class="bento" aria-label="SAP MCP hosted server overview">
|
|
1073
|
-
<section class="card strong span-4">
|
|
1074
|
-
<h3>Facilitator Volume</h3>
|
|
1075
|
-
<div class="metric">${escapeHtml(formatUsd(paymentStats.totalVolumeUsd))}</div>
|
|
1076
|
-
<p class="caption">Estimated settled x402/pay.sh tool volume recorded by the hosted payment ledger.</p>
|
|
1077
|
-
</section>
|
|
1078
|
-
|
|
1079
|
-
<section class="card strong span-4">
|
|
1080
|
-
<h3>Total Settlements</h3>
|
|
1081
|
-
<div class="metric">${escapeHtml(formatInteger(paymentStats.totalSettlements))}</div>
|
|
1082
|
-
<p class="caption">${escapeHtml(formatLastSettlement(paymentStats))}</p>
|
|
1083
|
-
</section>
|
|
1084
|
-
|
|
1085
|
-
<section class="card span-4">
|
|
1086
|
-
<h3>Payment Requests</h3>
|
|
1087
|
-
<div class="metric">${escapeHtml(formatInteger(paymentStats.totalPaymentRequests))}</div>
|
|
1088
|
-
<p class="caption"><span class="status">${escapeHtml(formatInteger(paymentStats.totalVerifiedPayments))}</span> verified, <span class="warning">${escapeHtml(formatInteger(paymentStats.totalFailedSettlements))}</span> failed settlements.</p>
|
|
1089
|
-
</section>
|
|
1090
|
-
|
|
1091
|
-
<section class="card span-7">
|
|
1092
|
-
<h2>Install Wizard</h2>
|
|
1093
|
-
<p>Create a local SAP MCP profile, signer, policy limits, and optional client injection. Hosted users still sign x402/pay.sh and value-moving operations from their own machine or external signer.</p>
|
|
1094
|
-
<div class="command"><code>${escapeHtml(installScriptCommand)}</code></div>
|
|
1095
|
-
<div class="command"><code>${escapeHtml(wizardCommand)}</code></div>
|
|
1096
|
-
</section>
|
|
1097
|
-
|
|
1098
|
-
<section class="card span-5">
|
|
1099
|
-
<h2>Native Downloads</h2>
|
|
1100
|
-
<p>Use the desktop wizard when you want a guided setup without touching config files by hand.</p>
|
|
1101
|
-
<div class="download-grid">
|
|
1102
|
-
<a class="download-link" href="${escapeHtml(info.downloads.desktopWizard.windowsX64Setup)}"><strong>Windows</strong><span>x64 setup .exe</span></a>
|
|
1103
|
-
<a class="download-link" href="${escapeHtml(info.downloads.desktopWizard.macosArm64Dmg)}"><strong>macOS</strong><span>Apple Silicon .dmg</span></a>
|
|
1104
|
-
<a class="download-link" href="${escapeHtml(info.downloads.desktopWizard.linuxX64TarGz)}"><strong>Linux</strong><span>x64 tar.gz</span></a>
|
|
1105
|
-
</div>
|
|
1106
|
-
<p class="caption">Machine-readable links: <a href="${escapeHtml(info.endpoints.wizardDownloads)}">/wizard/downloads.json</a>.</p>
|
|
1107
|
-
</section>
|
|
1108
|
-
|
|
1109
|
-
<section class="card span-5">
|
|
1110
|
-
<h2>Payments</h2>
|
|
1111
|
-
<ul class="list">
|
|
1112
|
-
<li><strong>x402:</strong> paid MCP tool calls return HTTP 402 with payment requirements, then settle through the OOBE facilitator.</li>
|
|
1113
|
-
<li><strong>pay.sh:</strong> public provider YAML is available at <a href="${escapeHtml(info.endpoints.payShProvider)}">/pay/provider.yml</a> for catalog and proxy workflows.</li>
|
|
1114
|
-
<li><strong>Local stdio:</strong> free by default; monetization applies to hosted remote usage.</li>
|
|
1115
|
-
</ul>
|
|
1116
|
-
</section>
|
|
1117
|
-
|
|
1118
|
-
<section class="card strong span-12">
|
|
1119
|
-
<h2>x402 Paid-Call Plugin For Agents</h2>
|
|
1120
|
-
<p>Agents that can reach hosted MCP but cannot replay x402 challenges natively can install the wizard addon or invoke the local paid-call helper directly. The helper signs the payment payload locally and retries the hosted tool call without sending keypair bytes to OOBE.</p>
|
|
1121
|
-
<div class="command"><code>Addon path: ${escapeHtml(X402_PAID_CALL_ADDON_PATH)}</code></div>
|
|
1122
|
-
<div class="command"><code>${escapeHtml(x402PaidCallCommand)}</code></div>
|
|
1123
|
-
</section>
|
|
1124
|
-
|
|
1125
|
-
<section class="card span-8">
|
|
1126
|
-
<h2>Endpoints</h2>
|
|
1127
|
-
<div class="endpoint-list">
|
|
1128
|
-
<div class="endpoint-row"><span class="method">POST</span><code>${escapeHtml(info.endpoints.mcp)}</code></div>
|
|
1129
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.docs)}</code></div>
|
|
1130
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.health)}</code></div>
|
|
1131
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.serverInfo)}</code></div>
|
|
1132
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.openApi)}</code></div>
|
|
1133
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.x402Discovery)}</code></div>
|
|
1134
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.payShProvider)}</code></div>
|
|
1135
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.agentCard)}</code></div>
|
|
1136
|
-
<div class="endpoint-row"><span class="method">GET</span><code>${escapeHtml(info.endpoints.wizardDescriptor)}</code></div>
|
|
1137
|
-
</div>
|
|
1138
|
-
</section>
|
|
1139
|
-
|
|
1140
|
-
<section class="card span-4">
|
|
1141
|
-
<h2>Security Boundary</h2>
|
|
1142
|
-
<ul class="list">
|
|
1143
|
-
<li>Keypair bytes are never exposed by public endpoints.</li>
|
|
1144
|
-
<li>RPC secrets are redacted from public metadata and UI.</li>
|
|
1145
|
-
<li>Paid tools require payment before execution.</li>
|
|
1146
|
-
</ul>
|
|
1147
|
-
</section>
|
|
1148
|
-
</div>
|
|
1149
|
-
<p class="footer">Use <a href="/server.json">/server.json</a> for machine-readable public metadata. MCP clients should connect to <a href="/mcp">/mcp</a> with <code>Accept: application/json, text/event-stream</code>.</p>
|
|
1150
|
-
</main>
|
|
1151
|
-
</body>
|
|
1152
|
-
</html>`;
|
|
983
|
+
return renderLandingPage({
|
|
984
|
+
endpoint,
|
|
985
|
+
info,
|
|
986
|
+
paymentStats,
|
|
987
|
+
pageUrl,
|
|
988
|
+
title,
|
|
989
|
+
installScriptCommand: 'curl -fsSL https://mcp.sap.oobeprotocol.ai/wizard/install.sh | sh',
|
|
990
|
+
wizardCommand: WIZARD_NPM_COMMAND,
|
|
991
|
+
paidCallCommand: X402_PAID_CALL_NPX_COMMAND,
|
|
992
|
+
paidCallAddonPath: X402_PAID_CALL_ADDON_PATH,
|
|
993
|
+
});
|
|
1153
994
|
}
|
|
1154
995
|
/**
|
|
1155
996
|
* @name buildDocsHtml
|
|
@@ -1268,30 +1109,6 @@ function acceptsHtmlPreview(req) {
|
|
|
1268
1109
|
const accept = String(req.headers.accept ?? '');
|
|
1269
1110
|
return accept.includes('text/html') && !accept.includes('text/event-stream') && !accept.includes('application/json');
|
|
1270
1111
|
}
|
|
1271
|
-
function formatUsd(value) {
|
|
1272
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
1273
|
-
return '$0.00';
|
|
1274
|
-
}
|
|
1275
|
-
return `$${value.toLocaleString('en-US', {
|
|
1276
|
-
minimumFractionDigits: value < 1 ? 3 : 2,
|
|
1277
|
-
maximumFractionDigits: value < 1 ? 3 : 2,
|
|
1278
|
-
})}`;
|
|
1279
|
-
}
|
|
1280
|
-
function formatInteger(value) {
|
|
1281
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
1282
|
-
return '0';
|
|
1283
|
-
}
|
|
1284
|
-
return Math.trunc(value).toLocaleString('en-US');
|
|
1285
|
-
}
|
|
1286
|
-
function formatLastSettlement(stats) {
|
|
1287
|
-
if (!stats.ledgerAvailable) {
|
|
1288
|
-
return 'Ledger not initialized yet. Metrics appear after the first paid hosted call.';
|
|
1289
|
-
}
|
|
1290
|
-
if (!stats.lastSettlementAt) {
|
|
1291
|
-
return 'No settlements yet. Paid requests will update this card after successful settlement.';
|
|
1292
|
-
}
|
|
1293
|
-
return `Last settlement: ${stats.lastSettlementAt}`;
|
|
1294
|
-
}
|
|
1295
1112
|
/**
|
|
1296
1113
|
* @name escapeHtml
|
|
1297
1114
|
* @description Escapes dynamic strings before embedding them in public HTML metadata.
|