@solvapay/mcp 0.1.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/LICENSE.md +21 -0
- package/README.md +131 -0
- package/dist/index.cjs +221 -0
- package/dist/index.d.cts +180 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.js +201 -0
- package/package.json +65 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 SolvaPay Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @solvapay/mcp
|
|
2
|
+
|
|
3
|
+
Official `@modelcontextprotocol/sdk` + `@modelcontextprotocol/ext-apps`
|
|
4
|
+
adapter for the SolvaPay MCP toolbox.
|
|
5
|
+
|
|
6
|
+
This is the only SolvaPay package that imports `@modelcontextprotocol/*`.
|
|
7
|
+
Framework-neutral contracts (tool names, descriptors, paywall meta,
|
|
8
|
+
OAuth discovery JSON, JWT helpers) live in
|
|
9
|
+
[`@solvapay/mcp-core`](../mcp-core) so alternative adapters
|
|
10
|
+
(`fastmcp`, raw JSON-RPC) can reuse the same contract. Runtime-specific
|
|
11
|
+
OAuth middleware lives alongside:
|
|
12
|
+
|
|
13
|
+
- [`@solvapay/mcp-express`](../mcp-express) — Node `(req, res, next)`.
|
|
14
|
+
- [`@solvapay/mcp-fetch`](../mcp-fetch) — Web standards `(req: Request) => Promise<Response>`.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @solvapay/mcp @solvapay/server \
|
|
20
|
+
@modelcontextprotocol/sdk @modelcontextprotocol/ext-apps zod
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Importing
|
|
24
|
+
|
|
25
|
+
Everything you need for a paywalled tool lives in this package:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import {
|
|
29
|
+
createSolvaPayMcpServer,
|
|
30
|
+
registerPayableTool,
|
|
31
|
+
type ResponseContext,
|
|
32
|
+
type NudgeSpec,
|
|
33
|
+
} from '@solvapay/mcp'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Reach into `@solvapay/mcp-core` directly only if you're writing a
|
|
37
|
+
framework adapter (`fastmcp`, raw JSON-RPC).
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createSolvaPayMcpServer } from '@solvapay/mcp'
|
|
43
|
+
import { createSolvaPay } from '@solvapay/server'
|
|
44
|
+
import { z } from 'zod'
|
|
45
|
+
|
|
46
|
+
const solvaPay = createSolvaPay({ apiKey: process.env.SOLVAPAY_SECRET_KEY! })
|
|
47
|
+
|
|
48
|
+
const server = createSolvaPayMcpServer({
|
|
49
|
+
solvaPay,
|
|
50
|
+
productRef: 'prd_video',
|
|
51
|
+
resourceUri: 'ui://my-app/mcp-app.html',
|
|
52
|
+
htmlPath: './dist/mcp-app.html',
|
|
53
|
+
publicBaseUrl: 'https://my-app.example.com',
|
|
54
|
+
additionalTools: ({ registerPayable }) => {
|
|
55
|
+
registerPayable('create_video', {
|
|
56
|
+
schema: { prompt: z.string() },
|
|
57
|
+
description: 'Generate a short video from a text prompt.',
|
|
58
|
+
handler: async ({ prompt }, ctx) => {
|
|
59
|
+
const videoUrl = await generateVideo(prompt)
|
|
60
|
+
return ctx.respond({ videoUrl })
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
One call wires the full SolvaPay transport surface (`check_purchase`,
|
|
68
|
+
`create_payment_intent`, `process_payment`, `open_checkout`, etc.), the
|
|
69
|
+
UI resource with the Stripe CSP baseline, and any integrator-defined
|
|
70
|
+
tools via `additionalTools`.
|
|
71
|
+
|
|
72
|
+
## Handler contract
|
|
73
|
+
|
|
74
|
+
`registerPayable` handlers receive parsed `args` (inferred from
|
|
75
|
+
`schema` when provided) and a `ResponseContext`. They must return the
|
|
76
|
+
branded envelope produced by `ctx.respond(data, options?)`.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
handler: async ({ prompt }, ctx) => {
|
|
80
|
+
const video = await generate(prompt)
|
|
81
|
+
// Attach an upsell nudge when the customer is low on credits.
|
|
82
|
+
if (ctx.customer.balance < 500) {
|
|
83
|
+
return ctx.respond({ videoUrl: video.url }, {
|
|
84
|
+
nudge: { kind: 'low-balance', message: 'Running low on credits' },
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
return ctx.respond({ videoUrl: video.url })
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The [`ctx.respond()` V1 spec](../../docs/spec/ctx-respond-v1.md) has the
|
|
92
|
+
full surface. The TL;DR:
|
|
93
|
+
|
|
94
|
+
- `ctx.customer` — cached customer snapshot (≤10s stale). Read
|
|
95
|
+
`balance` / `remaining` / `plan` to branch on usage; call
|
|
96
|
+
`ctx.customer.fresh()` for a fresh fetch when staleness matters.
|
|
97
|
+
- `ctx.respond(data, options?)` — returns a branded envelope. `options`
|
|
98
|
+
carries `text` (override `content[0].text`), `nudge` (inline upsell
|
|
99
|
+
strip), and the reserved `units` (V1.1 variable-unit billing — V1
|
|
100
|
+
silently ignores).
|
|
101
|
+
- `ctx.gate(reason?)` — stops handler execution and routes a paywall
|
|
102
|
+
response through the adapter's `formatGate` channel. Rare — the
|
|
103
|
+
SDK normally fires the paywall automatically via `payable().mcp()`
|
|
104
|
+
pre-check.
|
|
105
|
+
- `ctx.emit(block)` / `ctx.progress(...)` / `ctx.signal` — reserved
|
|
106
|
+
surface. V1 queues (emit) or no-ops (progress / signal); V1.1 wires
|
|
107
|
+
them to SSE and transport cancellation without code changes.
|
|
108
|
+
|
|
109
|
+
## What's in the box
|
|
110
|
+
|
|
111
|
+
| Export | Use when |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `createSolvaPayMcpServer(opts)` | You want the batteries-included `McpServer` with every SolvaPay tool registered |
|
|
114
|
+
| `registerPayableTool(server, name, opts)` | You want to add a paywall-protected tool to an existing `McpServer`. `_meta.ui` is attached per-result on paywall and nudge responses only, so the iframe opens only when there's something to show. |
|
|
115
|
+
|
|
116
|
+
## Peer dependencies
|
|
117
|
+
|
|
118
|
+
| Peer | Why |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `@modelcontextprotocol/sdk` | The `McpServer` this package builds and returns |
|
|
121
|
+
| `@modelcontextprotocol/ext-apps` | `registerAppTool`, `registerAppResource`, the `RESOURCE_MIME_TYPE` constant |
|
|
122
|
+
| `@solvapay/mcp-core` | Neutral contracts and descriptor builder |
|
|
123
|
+
| `@solvapay/server` | `SolvaPay` factory + `PaywallError` runtime |
|
|
124
|
+
| `zod` | Tool `inputSchema` shape (required by the ext-apps helpers) |
|
|
125
|
+
|
|
126
|
+
## Want to adapt another framework?
|
|
127
|
+
|
|
128
|
+
Use [`@solvapay/mcp-core`](../mcp-core) directly. `buildSolvaPayDescriptors`
|
|
129
|
+
returns a framework-neutral bundle; map it onto your framework's
|
|
130
|
+
`registerTool` / `registerResource` API in ~60 lines. This package is
|
|
131
|
+
itself that mapper for the official SDK.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createSolvaPayMcpServer: () => createSolvaPayMcpServer,
|
|
24
|
+
registerPayableTool: () => registerPayableTool
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/server.ts
|
|
29
|
+
var import_server2 = require("@modelcontextprotocol/ext-apps/server");
|
|
30
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
31
|
+
var import_mcp_core2 = require("@solvapay/mcp-core");
|
|
32
|
+
|
|
33
|
+
// src/registerPayableTool.ts
|
|
34
|
+
var import_server = require("@modelcontextprotocol/ext-apps/server");
|
|
35
|
+
var import_mcp_core = require("@solvapay/mcp-core");
|
|
36
|
+
function registerPayableTool(server, name, options) {
|
|
37
|
+
const {
|
|
38
|
+
solvaPay,
|
|
39
|
+
resourceUri,
|
|
40
|
+
schema,
|
|
41
|
+
product,
|
|
42
|
+
title,
|
|
43
|
+
description,
|
|
44
|
+
handler,
|
|
45
|
+
buildBootstrap,
|
|
46
|
+
getCustomerRef,
|
|
47
|
+
meta,
|
|
48
|
+
annotations,
|
|
49
|
+
icons
|
|
50
|
+
} = options;
|
|
51
|
+
const protectedHandler = (0, import_mcp_core.buildPayableHandler)(
|
|
52
|
+
solvaPay,
|
|
53
|
+
{ product, resourceUri, buildBootstrap, getCustomerRef },
|
|
54
|
+
handler
|
|
55
|
+
);
|
|
56
|
+
const baseMeta = meta ?? {};
|
|
57
|
+
const baseUi = baseMeta.ui ?? {};
|
|
58
|
+
const hasIcons = icons !== void 0 && icons.length > 0;
|
|
59
|
+
const mergedUi = {
|
|
60
|
+
resourceUri,
|
|
61
|
+
...baseUi,
|
|
62
|
+
...hasIcons ? { icons } : {}
|
|
63
|
+
};
|
|
64
|
+
const toolMeta = { ...baseMeta, ui: mergedUi };
|
|
65
|
+
const effectiveAnnotations = {
|
|
66
|
+
readOnlyHint: true,
|
|
67
|
+
openWorldHint: true,
|
|
68
|
+
...annotations
|
|
69
|
+
};
|
|
70
|
+
return (0, import_server.registerAppTool)(
|
|
71
|
+
server,
|
|
72
|
+
name,
|
|
73
|
+
// Note: `registerAppTool`'s config type is stricter than ours —
|
|
74
|
+
// casting so `title` / `description` stay optional and the input
|
|
75
|
+
// schema flows through correctly at the registration layer.
|
|
76
|
+
{
|
|
77
|
+
...title !== void 0 ? { title } : {},
|
|
78
|
+
...description !== void 0 ? { description } : {},
|
|
79
|
+
...schema !== void 0 ? { inputSchema: schema } : {},
|
|
80
|
+
_meta: toolMeta,
|
|
81
|
+
annotations: effectiveAnnotations,
|
|
82
|
+
...icons !== void 0 && icons.length > 0 ? { icons } : {}
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
+
},
|
|
85
|
+
async (args, extra) => await protectedHandler(args, extra)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/server.ts
|
|
90
|
+
function registerDescriptor(server, tool) {
|
|
91
|
+
const baseMeta = tool.meta ?? {};
|
|
92
|
+
const baseUi = baseMeta.ui ?? {};
|
|
93
|
+
const metaWithIcons = tool.icons && tool.icons.length > 0 ? { ...baseMeta, ui: { ...baseUi, icons: tool.icons } } : baseMeta;
|
|
94
|
+
(0, import_server2.registerAppTool)(
|
|
95
|
+
server,
|
|
96
|
+
tool.name,
|
|
97
|
+
{
|
|
98
|
+
...tool.title !== void 0 ? { title: tool.title } : {},
|
|
99
|
+
description: tool.description,
|
|
100
|
+
inputSchema: tool.inputSchema,
|
|
101
|
+
_meta: metaWithIcons,
|
|
102
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
|
|
103
|
+
...tool.icons !== void 0 ? { icons: tool.icons } : {}
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
},
|
|
106
|
+
// `SolvaPayCallToolResult` is a structural subset of the official
|
|
107
|
+
// SDK's `CallToolResult`; cast to erase the extra-narrow `resource`
|
|
108
|
+
// block typing the SDK expects on `{ type: 'resource' }` content.
|
|
109
|
+
async (args, extra) => await tool.handler(
|
|
110
|
+
args,
|
|
111
|
+
extra
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function registerPromptDescriptor(server, prompt) {
|
|
116
|
+
const config = { description: prompt.description };
|
|
117
|
+
if (prompt.title !== void 0) config.title = prompt.title;
|
|
118
|
+
if (prompt.argsSchema !== void 0) config.argsSchema = prompt.argsSchema;
|
|
119
|
+
server.registerPrompt(
|
|
120
|
+
prompt.name,
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
122
|
+
config,
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
+
async (args) => await prompt.handler(args ?? {})
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function registerDocsResource(server, docs) {
|
|
128
|
+
server.registerResource(
|
|
129
|
+
docs.name,
|
|
130
|
+
docs.uri,
|
|
131
|
+
{
|
|
132
|
+
...docs.title !== void 0 ? { title: docs.title } : {},
|
|
133
|
+
description: docs.description,
|
|
134
|
+
mimeType: docs.mimeType
|
|
135
|
+
},
|
|
136
|
+
async () => ({
|
|
137
|
+
contents: [
|
|
138
|
+
{
|
|
139
|
+
uri: docs.uri,
|
|
140
|
+
mimeType: docs.mimeType,
|
|
141
|
+
text: await docs.readBody()
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
function createSolvaPayMcpServer(options) {
|
|
148
|
+
const {
|
|
149
|
+
additionalTools,
|
|
150
|
+
registerPrompts = true,
|
|
151
|
+
registerDocsResources = true,
|
|
152
|
+
serverName,
|
|
153
|
+
serverVersion = "1.0.0",
|
|
154
|
+
...descriptorOptions
|
|
155
|
+
} = options;
|
|
156
|
+
const { tools, resource, prompts, docsResources, buildBootstrapPayload } = (0, import_mcp_core2.buildSolvaPayDescriptors)(descriptorOptions);
|
|
157
|
+
const effectiveServerName = serverName ?? descriptorOptions.branding?.brandName ?? "solvapay-mcp-server";
|
|
158
|
+
const server = new import_mcp.McpServer({ name: effectiveServerName, version: serverVersion });
|
|
159
|
+
for (const tool of tools) {
|
|
160
|
+
registerDescriptor(server, tool);
|
|
161
|
+
}
|
|
162
|
+
if (registerPrompts) {
|
|
163
|
+
for (const prompt of prompts) {
|
|
164
|
+
registerPromptDescriptor(server, prompt);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (registerDocsResources) {
|
|
168
|
+
for (const docs of docsResources) {
|
|
169
|
+
registerDocsResource(server, docs);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
(0, import_server2.registerAppResource)(
|
|
173
|
+
server,
|
|
174
|
+
resource.uri,
|
|
175
|
+
resource.uri,
|
|
176
|
+
{
|
|
177
|
+
mimeType: import_server2.RESOURCE_MIME_TYPE,
|
|
178
|
+
_meta: {
|
|
179
|
+
ui: {
|
|
180
|
+
csp: resource.csp,
|
|
181
|
+
prefersBorder: true
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
async () => ({
|
|
186
|
+
contents: [
|
|
187
|
+
{
|
|
188
|
+
uri: resource.uri,
|
|
189
|
+
mimeType: import_server2.RESOURCE_MIME_TYPE,
|
|
190
|
+
text: await resource.readHtml(),
|
|
191
|
+
_meta: {
|
|
192
|
+
ui: {
|
|
193
|
+
csp: resource.csp,
|
|
194
|
+
prefersBorder: true
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
if (additionalTools) {
|
|
202
|
+
const { solvaPay, productRef, resourceUri } = descriptorOptions;
|
|
203
|
+
const registerPayable = (name, opts) => {
|
|
204
|
+
registerPayableTool(server, name, {
|
|
205
|
+
solvaPay,
|
|
206
|
+
resourceUri,
|
|
207
|
+
...opts,
|
|
208
|
+
product: opts.product ?? productRef,
|
|
209
|
+
buildBootstrap: opts.buildBootstrap ?? buildBootstrapPayload
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
additionalTools({ server, solvaPay, resourceUri, productRef, registerPayable });
|
|
214
|
+
}
|
|
215
|
+
return server;
|
|
216
|
+
}
|
|
217
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
218
|
+
0 && (module.exports = {
|
|
219
|
+
createSolvaPayMcpServer,
|
|
220
|
+
registerPayableTool
|
|
221
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { ZodRawShapeCompat, AnySchema, ShapeOutput, SchemaOutput } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
3
|
+
import { PayableHandler, BuildBootstrapPayloadFn, McpToolExtra, SolvaPayToolAnnotations, SolvaPayToolIcon, BuildSolvaPayDescriptorsOptions } from '@solvapay/mcp-core';
|
|
4
|
+
export { ContentBlock, CustomerSnapshot, NudgeSpec, PayableHandler, ResponseContext, ResponseOptions, ResponseResult } from '@solvapay/mcp-core';
|
|
5
|
+
import { SolvaPay } from '@solvapay/server';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `registerPayableTool(server, name, options)` — one-liner for registering
|
|
9
|
+
* a paywall-protected MCP tool on the official `@modelcontextprotocol/sdk`
|
|
10
|
+
* `McpServer`.
|
|
11
|
+
*
|
|
12
|
+
* Always advertises `_meta.ui.resourceUri` at the descriptor level so
|
|
13
|
+
* descriptor-reading hosts (MCPJam's `ResultsPanel`, MCP App inspector)
|
|
14
|
+
* learn the widget exists and open the MCP App iframe when a paywall or
|
|
15
|
+
* nudge response lands. `buildPayableHandler` stamps the same metadata
|
|
16
|
+
* on the result envelope as a secondary signal for hosts that key off
|
|
17
|
+
* tool-call results (Claude Desktop, mcp-ui, ChatGPT Apps).
|
|
18
|
+
*
|
|
19
|
+
* Mirrors the positional-`name` shape of `registerAppTool` to keep the
|
|
20
|
+
* convention consistent across the ecosystem.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Projects the tool's `schema` (raw shape or already-constructed
|
|
25
|
+
* schema) into the `args` type the handler receives. When no schema
|
|
26
|
+
* is provided, falls back to `Record<string, unknown>` so handlers
|
|
27
|
+
* can still destructure without losing type-check.
|
|
28
|
+
*/
|
|
29
|
+
type InferHandlerArgs<InputSchema> = [InputSchema] extends [undefined] ? Record<string, unknown> : InputSchema extends ZodRawShapeCompat ? ShapeOutput<InputSchema> : InputSchema extends AnySchema ? SchemaOutput<InputSchema> : Record<string, unknown>;
|
|
30
|
+
interface RegisterPayableToolOptions<InputSchema extends ZodRawShapeCompat | AnySchema | undefined = undefined, TData = unknown> {
|
|
31
|
+
/** The initialised SolvaPay instance used to build `payable({ product }).mcp(handler)`. */
|
|
32
|
+
solvaPay: SolvaPay;
|
|
33
|
+
/**
|
|
34
|
+
* UI resource URI the MCP host should open to render the paywall view.
|
|
35
|
+
* Typically `'ui://<app>/<resource>.html'`.
|
|
36
|
+
*/
|
|
37
|
+
resourceUri: string;
|
|
38
|
+
/** Zod-compatible input schema (raw shape or discriminated schema). */
|
|
39
|
+
schema?: InputSchema;
|
|
40
|
+
/** SolvaPay product ref to protect this tool against. */
|
|
41
|
+
product: string;
|
|
42
|
+
/** Optional human-readable tool title for MCP listings. */
|
|
43
|
+
title?: string;
|
|
44
|
+
/** Optional tool description surfaced to the model. */
|
|
45
|
+
description?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Business logic that runs once the caller is within limits. Receives
|
|
48
|
+
* parsed `args` (inferred from `schema` when provided) and a
|
|
49
|
+
* `ResponseContext`; must return the branded envelope produced by
|
|
50
|
+
* `ctx.respond(data, options?)`.
|
|
51
|
+
*
|
|
52
|
+
* `ctx` surfaces:
|
|
53
|
+
* - `ctx.customer` — cached snapshot (`balance`, `remaining`, `plan`,
|
|
54
|
+
* `.fresh()` for a round-trip).
|
|
55
|
+
* - `ctx.product` — bootstrap product projection.
|
|
56
|
+
* - `ctx.respond(data, options?)` — return an envelope. `options`
|
|
57
|
+
* carries `text` (override `content[0].text`), `nudge` (inline
|
|
58
|
+
* upsell strip), and the reserved `units` (V1.1 variable billing —
|
|
59
|
+
* V1 silently ignores).
|
|
60
|
+
* - `ctx.gate(reason?)` — stops handler execution and emits a
|
|
61
|
+
* paywall response through the adapter's `formatGate` channel.
|
|
62
|
+
* Rare — the SDK normally fires the paywall automatically via
|
|
63
|
+
* `payable().mcp()` pre-check.
|
|
64
|
+
* - `ctx.emit(block)` / `ctx.progress(...)` / `ctx.signal` — reserved
|
|
65
|
+
* streaming surface. V1 queues (emit) or no-ops (progress / signal);
|
|
66
|
+
* V1.1 wires them to SSE and transport cancellation.
|
|
67
|
+
*
|
|
68
|
+
* Throwing anything other than `PaywallError` surfaces as a tool-level
|
|
69
|
+
* error via `formatError`.
|
|
70
|
+
*/
|
|
71
|
+
handler: PayableHandler<InferHandlerArgs<InputSchema>, TData>;
|
|
72
|
+
/**
|
|
73
|
+
* Builds the full `BootstrapPayload` embedded on paywall results so
|
|
74
|
+
* the React shell renders the paywall view directly from the gate
|
|
75
|
+
* response. Wire from
|
|
76
|
+
* `buildSolvaPayDescriptors(...).buildBootstrapPayload`.
|
|
77
|
+
*/
|
|
78
|
+
buildBootstrap?: BuildBootstrapPayloadFn;
|
|
79
|
+
/**
|
|
80
|
+
* Override customer-ref extraction. Defaults to the MCP adapter's
|
|
81
|
+
* behavior (reads `extra.authInfo.extra.customer_ref`).
|
|
82
|
+
*/
|
|
83
|
+
getCustomerRef?: (args: Record<string, unknown>, extra?: McpToolExtra) => string | Promise<string>;
|
|
84
|
+
/**
|
|
85
|
+
* Additional `_meta` merged onto the tool **descriptor** (the tool
|
|
86
|
+
* advertisement returned by `tools/list`).
|
|
87
|
+
*
|
|
88
|
+
* `registerPayableTool` always injects `ui.resourceUri` at the
|
|
89
|
+
* descriptor level so hosts that read widget metadata from
|
|
90
|
+
* `tools/list` (MCPJam's `ResultsPanel` reads
|
|
91
|
+
* `toolMeta.ui.resourceUri` from the tool definition) can open the
|
|
92
|
+
* MCP App iframe when a paywall/nudge response lands. Merchant
|
|
93
|
+
* `meta.ui.resourceUri` overrides the default if supplied.
|
|
94
|
+
*/
|
|
95
|
+
meta?: Record<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Portable MCP tool annotations. Defaults to
|
|
98
|
+
* `{ readOnlyHint: true, openWorldHint: true }` — sensible for a
|
|
99
|
+
* paywalled *data* tool that reads from the merchant's backend.
|
|
100
|
+
* Override for tools that mutate state (e.g. `submit_order`) with
|
|
101
|
+
* `annotations: { readOnlyHint: false, destructiveHint: true }`.
|
|
102
|
+
*/
|
|
103
|
+
annotations?: SolvaPayToolAnnotations;
|
|
104
|
+
/**
|
|
105
|
+
* Brand icons surfaced on `tools/list`. Hosts that read tool
|
|
106
|
+
* metadata for the chrome strip (ChatGPT, Claude Desktop) swap the
|
|
107
|
+
* default placeholder for this asset. Pass a square logomark for
|
|
108
|
+
* best results. Merchants typically share one `icons[]` across
|
|
109
|
+
* every tool — consider a single branding source at the server
|
|
110
|
+
* level.
|
|
111
|
+
*/
|
|
112
|
+
icons?: SolvaPayToolIcon[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Register a paywall-protected tool on an MCP server.
|
|
116
|
+
*/
|
|
117
|
+
declare function registerPayableTool<InputSchema extends ZodRawShapeCompat | AnySchema | undefined = undefined, TData = unknown>(server: McpServer, name: string, options: RegisterPayableToolOptions<InputSchema, TData>): RegisteredTool;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* `createSolvaPayMcpServer` — batteries-included factory that
|
|
121
|
+
* registers the full SolvaPay transport + bootstrap tool surface on a
|
|
122
|
+
* fresh `McpServer` from the official `@modelcontextprotocol/sdk`,
|
|
123
|
+
* plus the UI resource the `open_*` tools reference.
|
|
124
|
+
*
|
|
125
|
+
* Internals are a thin mapper over `buildSolvaPayDescriptors` from
|
|
126
|
+
* `@solvapay/mcp-core` — this package is the only one importing
|
|
127
|
+
* `@modelcontextprotocol/*`.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Callback fired from the `additionalTools` hook with helpers bound for
|
|
132
|
+
* the current server + `solvaPay` instance.
|
|
133
|
+
*/
|
|
134
|
+
interface AdditionalToolsContext {
|
|
135
|
+
server: McpServer;
|
|
136
|
+
solvaPay: SolvaPay;
|
|
137
|
+
resourceUri: string;
|
|
138
|
+
productRef: string;
|
|
139
|
+
/**
|
|
140
|
+
* `registerPayableTool` bound with `solvaPay` + `resourceUri` already
|
|
141
|
+
* provided, and `product` defaulting to the server's `productRef`.
|
|
142
|
+
*
|
|
143
|
+
* Zod `schema` flows through to the handler's `args` parameter so
|
|
144
|
+
* merchants get inferred arg types without a second declaration.
|
|
145
|
+
*/
|
|
146
|
+
registerPayable: <InputSchema extends ZodRawShapeCompat | AnySchema | undefined = undefined, TData = unknown>(name: string, options: Omit<RegisterPayableToolOptions<InputSchema, TData>, 'solvaPay' | 'resourceUri' | 'product'> & {
|
|
147
|
+
product?: string;
|
|
148
|
+
}) => void;
|
|
149
|
+
}
|
|
150
|
+
interface CreateSolvaPayMcpServerOptions extends BuildSolvaPayDescriptorsOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Integrator hook to register non-SolvaPay tools. The callback receives
|
|
153
|
+
* the built server plus a `registerPayable` helper bound for this
|
|
154
|
+
* instance.
|
|
155
|
+
*/
|
|
156
|
+
additionalTools?: (ctx: AdditionalToolsContext) => void;
|
|
157
|
+
/**
|
|
158
|
+
* Register the slash-command prompts (`/upgrade`, `/manage_account`,
|
|
159
|
+
* `/topup`, `/activate_plan`) built from the descriptor bundle.
|
|
160
|
+
* Defaults to `true` — the prompts are additive and silently ignored
|
|
161
|
+
* by hosts without prompt support.
|
|
162
|
+
*/
|
|
163
|
+
registerPrompts?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Register the narrated `docs://solvapay/overview.md` resource so
|
|
166
|
+
* agents can `resources/read` before trying a tool. Defaults to
|
|
167
|
+
* `true` — pure narration, no side-effects.
|
|
168
|
+
*/
|
|
169
|
+
registerDocsResources?: boolean;
|
|
170
|
+
/** Overrides the default `McpServer` name. */
|
|
171
|
+
serverName?: string;
|
|
172
|
+
/** Overrides the default `McpServer` version. */
|
|
173
|
+
serverVersion?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Build the MCP server and register the full SolvaPay tool surface.
|
|
177
|
+
*/
|
|
178
|
+
declare function createSolvaPayMcpServer(options: CreateSolvaPayMcpServerOptions): McpServer;
|
|
179
|
+
|
|
180
|
+
export { type AdditionalToolsContext, type CreateSolvaPayMcpServerOptions, type RegisterPayableToolOptions, createSolvaPayMcpServer, registerPayableTool };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { McpServer, RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { ZodRawShapeCompat, AnySchema, ShapeOutput, SchemaOutput } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
3
|
+
import { PayableHandler, BuildBootstrapPayloadFn, McpToolExtra, SolvaPayToolAnnotations, SolvaPayToolIcon, BuildSolvaPayDescriptorsOptions } from '@solvapay/mcp-core';
|
|
4
|
+
export { ContentBlock, CustomerSnapshot, NudgeSpec, PayableHandler, ResponseContext, ResponseOptions, ResponseResult } from '@solvapay/mcp-core';
|
|
5
|
+
import { SolvaPay } from '@solvapay/server';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `registerPayableTool(server, name, options)` — one-liner for registering
|
|
9
|
+
* a paywall-protected MCP tool on the official `@modelcontextprotocol/sdk`
|
|
10
|
+
* `McpServer`.
|
|
11
|
+
*
|
|
12
|
+
* Always advertises `_meta.ui.resourceUri` at the descriptor level so
|
|
13
|
+
* descriptor-reading hosts (MCPJam's `ResultsPanel`, MCP App inspector)
|
|
14
|
+
* learn the widget exists and open the MCP App iframe when a paywall or
|
|
15
|
+
* nudge response lands. `buildPayableHandler` stamps the same metadata
|
|
16
|
+
* on the result envelope as a secondary signal for hosts that key off
|
|
17
|
+
* tool-call results (Claude Desktop, mcp-ui, ChatGPT Apps).
|
|
18
|
+
*
|
|
19
|
+
* Mirrors the positional-`name` shape of `registerAppTool` to keep the
|
|
20
|
+
* convention consistent across the ecosystem.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Projects the tool's `schema` (raw shape or already-constructed
|
|
25
|
+
* schema) into the `args` type the handler receives. When no schema
|
|
26
|
+
* is provided, falls back to `Record<string, unknown>` so handlers
|
|
27
|
+
* can still destructure without losing type-check.
|
|
28
|
+
*/
|
|
29
|
+
type InferHandlerArgs<InputSchema> = [InputSchema] extends [undefined] ? Record<string, unknown> : InputSchema extends ZodRawShapeCompat ? ShapeOutput<InputSchema> : InputSchema extends AnySchema ? SchemaOutput<InputSchema> : Record<string, unknown>;
|
|
30
|
+
interface RegisterPayableToolOptions<InputSchema extends ZodRawShapeCompat | AnySchema | undefined = undefined, TData = unknown> {
|
|
31
|
+
/** The initialised SolvaPay instance used to build `payable({ product }).mcp(handler)`. */
|
|
32
|
+
solvaPay: SolvaPay;
|
|
33
|
+
/**
|
|
34
|
+
* UI resource URI the MCP host should open to render the paywall view.
|
|
35
|
+
* Typically `'ui://<app>/<resource>.html'`.
|
|
36
|
+
*/
|
|
37
|
+
resourceUri: string;
|
|
38
|
+
/** Zod-compatible input schema (raw shape or discriminated schema). */
|
|
39
|
+
schema?: InputSchema;
|
|
40
|
+
/** SolvaPay product ref to protect this tool against. */
|
|
41
|
+
product: string;
|
|
42
|
+
/** Optional human-readable tool title for MCP listings. */
|
|
43
|
+
title?: string;
|
|
44
|
+
/** Optional tool description surfaced to the model. */
|
|
45
|
+
description?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Business logic that runs once the caller is within limits. Receives
|
|
48
|
+
* parsed `args` (inferred from `schema` when provided) and a
|
|
49
|
+
* `ResponseContext`; must return the branded envelope produced by
|
|
50
|
+
* `ctx.respond(data, options?)`.
|
|
51
|
+
*
|
|
52
|
+
* `ctx` surfaces:
|
|
53
|
+
* - `ctx.customer` — cached snapshot (`balance`, `remaining`, `plan`,
|
|
54
|
+
* `.fresh()` for a round-trip).
|
|
55
|
+
* - `ctx.product` — bootstrap product projection.
|
|
56
|
+
* - `ctx.respond(data, options?)` — return an envelope. `options`
|
|
57
|
+
* carries `text` (override `content[0].text`), `nudge` (inline
|
|
58
|
+
* upsell strip), and the reserved `units` (V1.1 variable billing —
|
|
59
|
+
* V1 silently ignores).
|
|
60
|
+
* - `ctx.gate(reason?)` — stops handler execution and emits a
|
|
61
|
+
* paywall response through the adapter's `formatGate` channel.
|
|
62
|
+
* Rare — the SDK normally fires the paywall automatically via
|
|
63
|
+
* `payable().mcp()` pre-check.
|
|
64
|
+
* - `ctx.emit(block)` / `ctx.progress(...)` / `ctx.signal` — reserved
|
|
65
|
+
* streaming surface. V1 queues (emit) or no-ops (progress / signal);
|
|
66
|
+
* V1.1 wires them to SSE and transport cancellation.
|
|
67
|
+
*
|
|
68
|
+
* Throwing anything other than `PaywallError` surfaces as a tool-level
|
|
69
|
+
* error via `formatError`.
|
|
70
|
+
*/
|
|
71
|
+
handler: PayableHandler<InferHandlerArgs<InputSchema>, TData>;
|
|
72
|
+
/**
|
|
73
|
+
* Builds the full `BootstrapPayload` embedded on paywall results so
|
|
74
|
+
* the React shell renders the paywall view directly from the gate
|
|
75
|
+
* response. Wire from
|
|
76
|
+
* `buildSolvaPayDescriptors(...).buildBootstrapPayload`.
|
|
77
|
+
*/
|
|
78
|
+
buildBootstrap?: BuildBootstrapPayloadFn;
|
|
79
|
+
/**
|
|
80
|
+
* Override customer-ref extraction. Defaults to the MCP adapter's
|
|
81
|
+
* behavior (reads `extra.authInfo.extra.customer_ref`).
|
|
82
|
+
*/
|
|
83
|
+
getCustomerRef?: (args: Record<string, unknown>, extra?: McpToolExtra) => string | Promise<string>;
|
|
84
|
+
/**
|
|
85
|
+
* Additional `_meta` merged onto the tool **descriptor** (the tool
|
|
86
|
+
* advertisement returned by `tools/list`).
|
|
87
|
+
*
|
|
88
|
+
* `registerPayableTool` always injects `ui.resourceUri` at the
|
|
89
|
+
* descriptor level so hosts that read widget metadata from
|
|
90
|
+
* `tools/list` (MCPJam's `ResultsPanel` reads
|
|
91
|
+
* `toolMeta.ui.resourceUri` from the tool definition) can open the
|
|
92
|
+
* MCP App iframe when a paywall/nudge response lands. Merchant
|
|
93
|
+
* `meta.ui.resourceUri` overrides the default if supplied.
|
|
94
|
+
*/
|
|
95
|
+
meta?: Record<string, unknown>;
|
|
96
|
+
/**
|
|
97
|
+
* Portable MCP tool annotations. Defaults to
|
|
98
|
+
* `{ readOnlyHint: true, openWorldHint: true }` — sensible for a
|
|
99
|
+
* paywalled *data* tool that reads from the merchant's backend.
|
|
100
|
+
* Override for tools that mutate state (e.g. `submit_order`) with
|
|
101
|
+
* `annotations: { readOnlyHint: false, destructiveHint: true }`.
|
|
102
|
+
*/
|
|
103
|
+
annotations?: SolvaPayToolAnnotations;
|
|
104
|
+
/**
|
|
105
|
+
* Brand icons surfaced on `tools/list`. Hosts that read tool
|
|
106
|
+
* metadata for the chrome strip (ChatGPT, Claude Desktop) swap the
|
|
107
|
+
* default placeholder for this asset. Pass a square logomark for
|
|
108
|
+
* best results. Merchants typically share one `icons[]` across
|
|
109
|
+
* every tool — consider a single branding source at the server
|
|
110
|
+
* level.
|
|
111
|
+
*/
|
|
112
|
+
icons?: SolvaPayToolIcon[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Register a paywall-protected tool on an MCP server.
|
|
116
|
+
*/
|
|
117
|
+
declare function registerPayableTool<InputSchema extends ZodRawShapeCompat | AnySchema | undefined = undefined, TData = unknown>(server: McpServer, name: string, options: RegisterPayableToolOptions<InputSchema, TData>): RegisteredTool;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* `createSolvaPayMcpServer` — batteries-included factory that
|
|
121
|
+
* registers the full SolvaPay transport + bootstrap tool surface on a
|
|
122
|
+
* fresh `McpServer` from the official `@modelcontextprotocol/sdk`,
|
|
123
|
+
* plus the UI resource the `open_*` tools reference.
|
|
124
|
+
*
|
|
125
|
+
* Internals are a thin mapper over `buildSolvaPayDescriptors` from
|
|
126
|
+
* `@solvapay/mcp-core` — this package is the only one importing
|
|
127
|
+
* `@modelcontextprotocol/*`.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Callback fired from the `additionalTools` hook with helpers bound for
|
|
132
|
+
* the current server + `solvaPay` instance.
|
|
133
|
+
*/
|
|
134
|
+
interface AdditionalToolsContext {
|
|
135
|
+
server: McpServer;
|
|
136
|
+
solvaPay: SolvaPay;
|
|
137
|
+
resourceUri: string;
|
|
138
|
+
productRef: string;
|
|
139
|
+
/**
|
|
140
|
+
* `registerPayableTool` bound with `solvaPay` + `resourceUri` already
|
|
141
|
+
* provided, and `product` defaulting to the server's `productRef`.
|
|
142
|
+
*
|
|
143
|
+
* Zod `schema` flows through to the handler's `args` parameter so
|
|
144
|
+
* merchants get inferred arg types without a second declaration.
|
|
145
|
+
*/
|
|
146
|
+
registerPayable: <InputSchema extends ZodRawShapeCompat | AnySchema | undefined = undefined, TData = unknown>(name: string, options: Omit<RegisterPayableToolOptions<InputSchema, TData>, 'solvaPay' | 'resourceUri' | 'product'> & {
|
|
147
|
+
product?: string;
|
|
148
|
+
}) => void;
|
|
149
|
+
}
|
|
150
|
+
interface CreateSolvaPayMcpServerOptions extends BuildSolvaPayDescriptorsOptions {
|
|
151
|
+
/**
|
|
152
|
+
* Integrator hook to register non-SolvaPay tools. The callback receives
|
|
153
|
+
* the built server plus a `registerPayable` helper bound for this
|
|
154
|
+
* instance.
|
|
155
|
+
*/
|
|
156
|
+
additionalTools?: (ctx: AdditionalToolsContext) => void;
|
|
157
|
+
/**
|
|
158
|
+
* Register the slash-command prompts (`/upgrade`, `/manage_account`,
|
|
159
|
+
* `/topup`, `/activate_plan`) built from the descriptor bundle.
|
|
160
|
+
* Defaults to `true` — the prompts are additive and silently ignored
|
|
161
|
+
* by hosts without prompt support.
|
|
162
|
+
*/
|
|
163
|
+
registerPrompts?: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Register the narrated `docs://solvapay/overview.md` resource so
|
|
166
|
+
* agents can `resources/read` before trying a tool. Defaults to
|
|
167
|
+
* `true` — pure narration, no side-effects.
|
|
168
|
+
*/
|
|
169
|
+
registerDocsResources?: boolean;
|
|
170
|
+
/** Overrides the default `McpServer` name. */
|
|
171
|
+
serverName?: string;
|
|
172
|
+
/** Overrides the default `McpServer` version. */
|
|
173
|
+
serverVersion?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Build the MCP server and register the full SolvaPay tool surface.
|
|
177
|
+
*/
|
|
178
|
+
declare function createSolvaPayMcpServer(options: CreateSolvaPayMcpServerOptions): McpServer;
|
|
179
|
+
|
|
180
|
+
export { type AdditionalToolsContext, type CreateSolvaPayMcpServerOptions, type RegisterPayableToolOptions, createSolvaPayMcpServer, registerPayableTool };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// src/server.ts
|
|
2
|
+
import {
|
|
3
|
+
registerAppResource,
|
|
4
|
+
registerAppTool as registerAppTool2,
|
|
5
|
+
RESOURCE_MIME_TYPE
|
|
6
|
+
} from "@modelcontextprotocol/ext-apps/server";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import {
|
|
9
|
+
buildSolvaPayDescriptors
|
|
10
|
+
} from "@solvapay/mcp-core";
|
|
11
|
+
|
|
12
|
+
// src/registerPayableTool.ts
|
|
13
|
+
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
14
|
+
import {
|
|
15
|
+
buildPayableHandler
|
|
16
|
+
} from "@solvapay/mcp-core";
|
|
17
|
+
function registerPayableTool(server, name, options) {
|
|
18
|
+
const {
|
|
19
|
+
solvaPay,
|
|
20
|
+
resourceUri,
|
|
21
|
+
schema,
|
|
22
|
+
product,
|
|
23
|
+
title,
|
|
24
|
+
description,
|
|
25
|
+
handler,
|
|
26
|
+
buildBootstrap,
|
|
27
|
+
getCustomerRef,
|
|
28
|
+
meta,
|
|
29
|
+
annotations,
|
|
30
|
+
icons
|
|
31
|
+
} = options;
|
|
32
|
+
const protectedHandler = buildPayableHandler(
|
|
33
|
+
solvaPay,
|
|
34
|
+
{ product, resourceUri, buildBootstrap, getCustomerRef },
|
|
35
|
+
handler
|
|
36
|
+
);
|
|
37
|
+
const baseMeta = meta ?? {};
|
|
38
|
+
const baseUi = baseMeta.ui ?? {};
|
|
39
|
+
const hasIcons = icons !== void 0 && icons.length > 0;
|
|
40
|
+
const mergedUi = {
|
|
41
|
+
resourceUri,
|
|
42
|
+
...baseUi,
|
|
43
|
+
...hasIcons ? { icons } : {}
|
|
44
|
+
};
|
|
45
|
+
const toolMeta = { ...baseMeta, ui: mergedUi };
|
|
46
|
+
const effectiveAnnotations = {
|
|
47
|
+
readOnlyHint: true,
|
|
48
|
+
openWorldHint: true,
|
|
49
|
+
...annotations
|
|
50
|
+
};
|
|
51
|
+
return registerAppTool(
|
|
52
|
+
server,
|
|
53
|
+
name,
|
|
54
|
+
// Note: `registerAppTool`'s config type is stricter than ours —
|
|
55
|
+
// casting so `title` / `description` stay optional and the input
|
|
56
|
+
// schema flows through correctly at the registration layer.
|
|
57
|
+
{
|
|
58
|
+
...title !== void 0 ? { title } : {},
|
|
59
|
+
...description !== void 0 ? { description } : {},
|
|
60
|
+
...schema !== void 0 ? { inputSchema: schema } : {},
|
|
61
|
+
_meta: toolMeta,
|
|
62
|
+
annotations: effectiveAnnotations,
|
|
63
|
+
...icons !== void 0 && icons.length > 0 ? { icons } : {}
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
+
},
|
|
66
|
+
async (args, extra) => await protectedHandler(args, extra)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/server.ts
|
|
71
|
+
function registerDescriptor(server, tool) {
|
|
72
|
+
const baseMeta = tool.meta ?? {};
|
|
73
|
+
const baseUi = baseMeta.ui ?? {};
|
|
74
|
+
const metaWithIcons = tool.icons && tool.icons.length > 0 ? { ...baseMeta, ui: { ...baseUi, icons: tool.icons } } : baseMeta;
|
|
75
|
+
registerAppTool2(
|
|
76
|
+
server,
|
|
77
|
+
tool.name,
|
|
78
|
+
{
|
|
79
|
+
...tool.title !== void 0 ? { title: tool.title } : {},
|
|
80
|
+
description: tool.description,
|
|
81
|
+
inputSchema: tool.inputSchema,
|
|
82
|
+
_meta: metaWithIcons,
|
|
83
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
|
|
84
|
+
...tool.icons !== void 0 ? { icons: tool.icons } : {}
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
86
|
+
},
|
|
87
|
+
// `SolvaPayCallToolResult` is a structural subset of the official
|
|
88
|
+
// SDK's `CallToolResult`; cast to erase the extra-narrow `resource`
|
|
89
|
+
// block typing the SDK expects on `{ type: 'resource' }` content.
|
|
90
|
+
async (args, extra) => await tool.handler(
|
|
91
|
+
args,
|
|
92
|
+
extra
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
function registerPromptDescriptor(server, prompt) {
|
|
97
|
+
const config = { description: prompt.description };
|
|
98
|
+
if (prompt.title !== void 0) config.title = prompt.title;
|
|
99
|
+
if (prompt.argsSchema !== void 0) config.argsSchema = prompt.argsSchema;
|
|
100
|
+
server.registerPrompt(
|
|
101
|
+
prompt.name,
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
+
config,
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
async (args) => await prompt.handler(args ?? {})
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function registerDocsResource(server, docs) {
|
|
109
|
+
server.registerResource(
|
|
110
|
+
docs.name,
|
|
111
|
+
docs.uri,
|
|
112
|
+
{
|
|
113
|
+
...docs.title !== void 0 ? { title: docs.title } : {},
|
|
114
|
+
description: docs.description,
|
|
115
|
+
mimeType: docs.mimeType
|
|
116
|
+
},
|
|
117
|
+
async () => ({
|
|
118
|
+
contents: [
|
|
119
|
+
{
|
|
120
|
+
uri: docs.uri,
|
|
121
|
+
mimeType: docs.mimeType,
|
|
122
|
+
text: await docs.readBody()
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
})
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
function createSolvaPayMcpServer(options) {
|
|
129
|
+
const {
|
|
130
|
+
additionalTools,
|
|
131
|
+
registerPrompts = true,
|
|
132
|
+
registerDocsResources = true,
|
|
133
|
+
serverName,
|
|
134
|
+
serverVersion = "1.0.0",
|
|
135
|
+
...descriptorOptions
|
|
136
|
+
} = options;
|
|
137
|
+
const { tools, resource, prompts, docsResources, buildBootstrapPayload } = buildSolvaPayDescriptors(descriptorOptions);
|
|
138
|
+
const effectiveServerName = serverName ?? descriptorOptions.branding?.brandName ?? "solvapay-mcp-server";
|
|
139
|
+
const server = new McpServer({ name: effectiveServerName, version: serverVersion });
|
|
140
|
+
for (const tool of tools) {
|
|
141
|
+
registerDescriptor(server, tool);
|
|
142
|
+
}
|
|
143
|
+
if (registerPrompts) {
|
|
144
|
+
for (const prompt of prompts) {
|
|
145
|
+
registerPromptDescriptor(server, prompt);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (registerDocsResources) {
|
|
149
|
+
for (const docs of docsResources) {
|
|
150
|
+
registerDocsResource(server, docs);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
registerAppResource(
|
|
154
|
+
server,
|
|
155
|
+
resource.uri,
|
|
156
|
+
resource.uri,
|
|
157
|
+
{
|
|
158
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
159
|
+
_meta: {
|
|
160
|
+
ui: {
|
|
161
|
+
csp: resource.csp,
|
|
162
|
+
prefersBorder: true
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
async () => ({
|
|
167
|
+
contents: [
|
|
168
|
+
{
|
|
169
|
+
uri: resource.uri,
|
|
170
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
171
|
+
text: await resource.readHtml(),
|
|
172
|
+
_meta: {
|
|
173
|
+
ui: {
|
|
174
|
+
csp: resource.csp,
|
|
175
|
+
prefersBorder: true
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
if (additionalTools) {
|
|
183
|
+
const { solvaPay, productRef, resourceUri } = descriptorOptions;
|
|
184
|
+
const registerPayable = (name, opts) => {
|
|
185
|
+
registerPayableTool(server, name, {
|
|
186
|
+
solvaPay,
|
|
187
|
+
resourceUri,
|
|
188
|
+
...opts,
|
|
189
|
+
product: opts.product ?? productRef,
|
|
190
|
+
buildBootstrap: opts.buildBootstrap ?? buildBootstrapPayload
|
|
191
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
additionalTools({ server, solvaPay, resourceUri, productRef, registerPayable });
|
|
195
|
+
}
|
|
196
|
+
return server;
|
|
197
|
+
}
|
|
198
|
+
export {
|
|
199
|
+
createSolvaPayMcpServer,
|
|
200
|
+
registerPayableTool
|
|
201
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@solvapay/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official @modelcontextprotocol/sdk + @modelcontextprotocol/ext-apps adapter for the SolvaPay MCP toolbox (createSolvaPayMcpServer, registerPayableTool).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/solvapay/solvapay-sdk.git",
|
|
26
|
+
"directory": "packages/mcp"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.17"
|
|
30
|
+
},
|
|
31
|
+
"sideEffects": false,
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@solvapay/mcp-core": "0.1.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@modelcontextprotocol/ext-apps": "^1.5.0",
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
38
|
+
"zod": "^3.25.0 || ^4.0.0",
|
|
39
|
+
"@solvapay/server": "1.0.8-preview.10"
|
|
40
|
+
},
|
|
41
|
+
"peerDependenciesMeta": {
|
|
42
|
+
"zod": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@modelcontextprotocol/ext-apps": "^1.5.0",
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
|
+
"tsup": "^8.5.1",
|
|
50
|
+
"typescript": "^5.9.3",
|
|
51
|
+
"vitest": "^4.1.2",
|
|
52
|
+
"zod": "^4.3.6",
|
|
53
|
+
"@solvapay/server": "1.0.8-preview.10",
|
|
54
|
+
"@solvapay/test-utils": "^0.0.0"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"dev": "tsup --watch",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:unit": "vitest run",
|
|
61
|
+
"test:watch": "vitest",
|
|
62
|
+
"lint": "eslint src",
|
|
63
|
+
"lint:fix": "eslint src --fix"
|
|
64
|
+
}
|
|
65
|
+
}
|