@tinycloud/mcp 0.5.0-beta.6 → 0.5.0-beta.8
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/README.md +12 -5
- package/dist/cli.cjs +7 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +7 -4
- package/dist/cli.js.map +1 -1
- package/dist/http-cli.cjs +53 -78
- package/dist/http-cli.cjs.map +1 -1
- package/dist/http-cli.js +52 -75
- package/dist/http-cli.js.map +1 -1
- package/dist/http.cjs +53 -78
- package/dist/http.cjs.map +1 -1
- package/dist/http.js +52 -75
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +53 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +52 -75
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +7 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +7 -4
- package/dist/server.js.map +1 -1
- package/dist/tools.cjs +4 -1
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.js +4 -1
- package/dist/tools.js.map +1 -1
- package/generated/mcp-facts.json +1 -2
- package/package.json +3 -3
- package/skills/tinycloud-delegated-secrets/SKILL.md +1 -1
package/dist/http-cli.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var import_server4 = require("@modelcontextprotocol/server");
|
|
|
32
32
|
|
|
33
33
|
// src/oauth.ts
|
|
34
34
|
var import_server = require("@modelcontextprotocol/server");
|
|
35
|
+
var import_node_sdk = require("@tinycloud/node-sdk");
|
|
35
36
|
var import_jose = require("jose");
|
|
36
37
|
var OWNER_DIDS_CLAIM = "https://tinycloud.xyz/owner_dids";
|
|
37
38
|
function createOpenKeyTokenVerifier(config) {
|
|
@@ -80,11 +81,11 @@ function authInfo(token, payload, config) {
|
|
|
80
81
|
);
|
|
81
82
|
}
|
|
82
83
|
const clientId = firstString(payload.azp, payload.client_id) ?? "unknown";
|
|
83
|
-
const ownerDids =
|
|
84
|
-
if (ownerDids
|
|
84
|
+
const ownerDids = canonicalOwnerDids(payload[OWNER_DIDS_CLAIM]);
|
|
85
|
+
if (ownerDids === void 0) {
|
|
85
86
|
throw new import_server.OAuthError(
|
|
86
87
|
import_server.OAuthErrorCode.InvalidToken,
|
|
87
|
-
"The OpenKey access token has no TinyCloud owner identity."
|
|
88
|
+
"The OpenKey access token has no valid TinyCloud owner identity."
|
|
88
89
|
);
|
|
89
90
|
}
|
|
90
91
|
return {
|
|
@@ -99,6 +100,21 @@ function authInfo(token, payload, config) {
|
|
|
99
100
|
}
|
|
100
101
|
};
|
|
101
102
|
}
|
|
103
|
+
var OWNER_DID = /^did:pkh:eip155:([1-9][0-9]{0,15}):(0x[0-9a-fA-F]{40})$/;
|
|
104
|
+
function canonicalOwnerDids(claim) {
|
|
105
|
+
if (!Array.isArray(claim) || claim.length === 0) return void 0;
|
|
106
|
+
const ownerDids = [];
|
|
107
|
+
for (const value of claim) {
|
|
108
|
+
const match = typeof value === "string" ? OWNER_DID.exec(value) : null;
|
|
109
|
+
if (match === null) return void 0;
|
|
110
|
+
try {
|
|
111
|
+
ownerDids.push((0, import_node_sdk.pkhDid)(match[2], Number(match[1])));
|
|
112
|
+
} catch {
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return [...new Set(ownerDids)];
|
|
117
|
+
}
|
|
102
118
|
function firstString(...values) {
|
|
103
119
|
return values.find((value) => typeof value === "string" && value.length > 0);
|
|
104
120
|
}
|
|
@@ -107,7 +123,7 @@ function firstString(...values) {
|
|
|
107
123
|
var import_node_crypto = require("crypto");
|
|
108
124
|
var import_promises = require("fs/promises");
|
|
109
125
|
var import_node_path = require("path");
|
|
110
|
-
var
|
|
126
|
+
var import_node_sdk2 = require("@tinycloud/node-sdk");
|
|
111
127
|
var import_node_sdk_wasm = require("@tinycloud/node-sdk-wasm");
|
|
112
128
|
var import_operations = require("@tinycloud/operations");
|
|
113
129
|
var PROFILE = "agent";
|
|
@@ -221,8 +237,7 @@ var RemoteTenantStore = class {
|
|
|
221
237
|
if (pending.ownerDids.length > 0 && !pending.ownerDids.some((did) => samePrincipal(node.did, did))) {
|
|
222
238
|
throw new ApprovalOwnerMismatchError();
|
|
223
239
|
}
|
|
224
|
-
|
|
225
|
-
if (!permissionsCover(pending.permissions, granted)) {
|
|
240
|
+
if (!(0, import_node_sdk2.isCapabilitySubset)(pending.permissions, node.getVerifiedSessionCapabilities()).subset) {
|
|
226
241
|
throw new Error("The approved delegation does not cover the requested account access.");
|
|
227
242
|
}
|
|
228
243
|
const profile = await readJson(this.#profilePath(tenant.stateRoot));
|
|
@@ -238,14 +253,25 @@ var RemoteTenantStore = class {
|
|
|
238
253
|
}
|
|
239
254
|
async #storeDelegation(tenant, pending, data) {
|
|
240
255
|
if (pending.requestId === void 0) throw new Error("Authority request is missing.");
|
|
241
|
-
const { node } = await this.#restoreApproval(tenant, data);
|
|
256
|
+
const { node, session } = await this.#restoreApproval(tenant, data);
|
|
242
257
|
if (!pending.ownerDids.some((did) => samePrincipal(node.did, did))) throw new ApprovalOwnerMismatchError();
|
|
243
|
-
const delegation =
|
|
258
|
+
const delegation = (0, import_node_sdk2.siweRuntimeDelegation)(node, {
|
|
259
|
+
authorization: session.delegationHeader.Authorization,
|
|
260
|
+
cid: session.delegationCid,
|
|
261
|
+
spaceId: session.spaceId,
|
|
262
|
+
ownerAddress: session.address,
|
|
263
|
+
chainId: session.chainId,
|
|
264
|
+
siwe: session.siwe,
|
|
265
|
+
signature: session.signature,
|
|
266
|
+
host: this.#config.nodeHost
|
|
267
|
+
});
|
|
244
268
|
const result = await (0, import_operations.invokeOperation)(
|
|
245
269
|
"tinycloud.auth.import",
|
|
246
270
|
1,
|
|
247
271
|
{ profile: PROFILE, stateRoot: tenant.stateRoot },
|
|
248
272
|
{
|
|
273
|
+
kind: "tinycloud.auth.delegation",
|
|
274
|
+
version: 1,
|
|
249
275
|
requestId: pending.requestId,
|
|
250
276
|
delegationCid: delegation.cid,
|
|
251
277
|
delegation
|
|
@@ -255,12 +281,14 @@ var RemoteTenantStore = class {
|
|
|
255
281
|
}
|
|
256
282
|
/**
|
|
257
283
|
* Verify the OpenKey callback as a SIWE session for this tenant's key. The
|
|
258
|
-
*
|
|
259
|
-
*
|
|
284
|
+
* callback must carry the full signed proof (SIWE, signature, address and
|
|
285
|
+
* chain), so the WASM verifier always runs: it binds the authorization bytes
|
|
286
|
+
* and CID to the signed SIWE, and `node.did` is the wallet that signed
|
|
287
|
+
* exactly the delegation being stored.
|
|
260
288
|
*/
|
|
261
289
|
async #restoreApproval(tenant, data) {
|
|
262
290
|
const session = sessionFromCallback(data, tenant.jwk);
|
|
263
|
-
const node = new
|
|
291
|
+
const node = new import_node_sdk2.TinyCloudNode({ host: this.#config.nodeHost });
|
|
264
292
|
await node.restoreSession(session);
|
|
265
293
|
if (!samePrincipal(node.sessionDid, tenant.sessionDid)) {
|
|
266
294
|
throw new Error("The approved delegation targets a different session.");
|
|
@@ -281,7 +309,7 @@ var RemoteTenantStore = class {
|
|
|
281
309
|
const raw = await readJson(this.#sessionPath(tenant.stateRoot));
|
|
282
310
|
if (raw === null) return false;
|
|
283
311
|
try {
|
|
284
|
-
const node = new
|
|
312
|
+
const node = new import_node_sdk2.TinyCloudNode({ host: this.#config.nodeHost });
|
|
285
313
|
await node.restoreSession(raw);
|
|
286
314
|
return samePrincipal(node.sessionDid, tenant.sessionDid) && ownerDids.some((did) => samePrincipal(node.did, did));
|
|
287
315
|
} catch {
|
|
@@ -436,7 +464,7 @@ function ensureWasm() {
|
|
|
436
464
|
}
|
|
437
465
|
function sessionFromCallback(data, privateJwk) {
|
|
438
466
|
const delegationHeader = data.delegationHeader;
|
|
439
|
-
if (!isRecord(delegationHeader) || typeof delegationHeader.Authorization !== "string" || typeof data.delegationCid !== "string" || typeof data.spaceId !== "string" || typeof data.verificationMethod !== "string") {
|
|
467
|
+
if (!isRecord(delegationHeader) || typeof delegationHeader.Authorization !== "string" || typeof data.delegationCid !== "string" || typeof data.spaceId !== "string" || typeof data.verificationMethod !== "string" || typeof data.address !== "string" || typeof data.chainId !== "number" || typeof data.siwe !== "string" || typeof data.signature !== "string") {
|
|
440
468
|
throw new Error("OpenKey returned an incomplete delegation.");
|
|
441
469
|
}
|
|
442
470
|
return {
|
|
@@ -445,63 +473,12 @@ function sessionFromCallback(data, privateJwk) {
|
|
|
445
473
|
spaceId: data.spaceId,
|
|
446
474
|
jwk: privateJwk,
|
|
447
475
|
verificationMethod: data.verificationMethod,
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
function portableFromCallback(data, permissions, host) {
|
|
455
|
-
const primary = permissions.find((permission) => permission.service !== "tinycloud.encryption") ?? permissions[0];
|
|
456
|
-
if (primary === void 0) throw new Error("The authority request has no permissions.");
|
|
457
|
-
const session = sessionFromCallback(data, {});
|
|
458
|
-
const expiry = delegationExpiry(data);
|
|
459
|
-
return {
|
|
460
|
-
cid: session.delegationCid,
|
|
461
|
-
delegationHeader: session.delegationHeader,
|
|
462
|
-
spaceId: session.spaceId,
|
|
463
|
-
path: primary.path,
|
|
464
|
-
actions: [...primary.actions],
|
|
465
|
-
resources: permissions.map((permission) => ({
|
|
466
|
-
service: permission.service.startsWith("tinycloud.") ? permission.service.slice("tinycloud.".length) : permission.service,
|
|
467
|
-
space: permission.service === "tinycloud.encryption" ? requiredPermissionSpace(permission) : session.spaceId,
|
|
468
|
-
path: permission.path,
|
|
469
|
-
actions: [...permission.actions]
|
|
470
|
-
})),
|
|
471
|
-
expiry,
|
|
472
|
-
delegateDID: session.verificationMethod,
|
|
473
|
-
ownerAddress: typeof data.address === "string" ? data.address : "",
|
|
474
|
-
chainId: typeof data.chainId === "number" ? data.chainId : 1,
|
|
475
|
-
host
|
|
476
|
+
address: data.address,
|
|
477
|
+
chainId: data.chainId,
|
|
478
|
+
siwe: data.siwe,
|
|
479
|
+
signature: data.signature
|
|
476
480
|
};
|
|
477
481
|
}
|
|
478
|
-
function delegationExpiry(data) {
|
|
479
|
-
for (const key of ["expiry", "expiresAt", "expirationTime"]) {
|
|
480
|
-
const parsed = parseDate(data[key]);
|
|
481
|
-
if (parsed !== void 0) return parsed;
|
|
482
|
-
}
|
|
483
|
-
if (typeof data.siwe === "string") {
|
|
484
|
-
const match = data.siwe.match(/^Expiration Time:\s*(.+)$/im);
|
|
485
|
-
const parsed = parseDate(match?.[1]?.trim());
|
|
486
|
-
if (parsed !== void 0) return parsed;
|
|
487
|
-
}
|
|
488
|
-
throw new Error("OpenKey did not return a delegation expiry.");
|
|
489
|
-
}
|
|
490
|
-
function permissionsCover(requested, granted) {
|
|
491
|
-
return requested.every((request) => granted.some(
|
|
492
|
-
(grant) => normalizeService(grant.service) === normalizeService(request.service) && typeof grant.space === "string" && typeof request.space === "string" && sameSpace(grant.space, request.space) && grant.path === request.path && request.actions.every((action) => grant.actions.includes(action))
|
|
493
|
-
));
|
|
494
|
-
}
|
|
495
|
-
function sameSpace(granted, requested) {
|
|
496
|
-
return granted === requested || granted.endsWith(`:${requested}`);
|
|
497
|
-
}
|
|
498
|
-
function normalizeService(service) {
|
|
499
|
-
return service.startsWith("tinycloud.") ? service : `tinycloud.${service}`;
|
|
500
|
-
}
|
|
501
|
-
function requiredPermissionSpace(permission) {
|
|
502
|
-
if (typeof permission.space !== "string") throw new Error("The authority request has no space.");
|
|
503
|
-
return permission.space;
|
|
504
|
-
}
|
|
505
482
|
function publicJwk(jwk) {
|
|
506
483
|
const copy = { ...jwk };
|
|
507
484
|
for (const field of ["d", "p", "q", "dp", "dq", "qi", "oth", "k"]) delete copy[field];
|
|
@@ -510,7 +487,7 @@ function publicJwk(jwk) {
|
|
|
510
487
|
function samePrincipal(left, right) {
|
|
511
488
|
if (typeof left !== "string") return false;
|
|
512
489
|
try {
|
|
513
|
-
return (0,
|
|
490
|
+
return (0, import_node_sdk2.principalDidEquals)(left, right);
|
|
514
491
|
} catch {
|
|
515
492
|
return false;
|
|
516
493
|
}
|
|
@@ -524,11 +501,6 @@ function isPermissionEntry(value) {
|
|
|
524
501
|
function isRecord(value) {
|
|
525
502
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
526
503
|
}
|
|
527
|
-
function parseDate(value) {
|
|
528
|
-
if (typeof value !== "string" && typeof value !== "number") return void 0;
|
|
529
|
-
const date = new Date(typeof value === "number" && value < 1e10 ? value * 1e3 : value);
|
|
530
|
-
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
531
|
-
}
|
|
532
504
|
async function secureDirectory(path) {
|
|
533
505
|
await (0, import_promises.mkdir)(path, { recursive: true, mode: 448 });
|
|
534
506
|
await (0, import_promises.chmod)(path, 448);
|
|
@@ -703,7 +675,10 @@ var TOOL_BINDINGS = [
|
|
|
703
675
|
readOnlyHint: false,
|
|
704
676
|
idempotentHint: true,
|
|
705
677
|
destructiveHint: false,
|
|
706
|
-
openWorldHint: false
|
|
678
|
+
openWorldHint: false,
|
|
679
|
+
// Hosted delegations must arrive through the owner/request-bound OpenKey callback,
|
|
680
|
+
// not a caller-supplied import artifact.
|
|
681
|
+
surfaces: ["local"]
|
|
707
682
|
},
|
|
708
683
|
{
|
|
709
684
|
name: "tinycloud_account_spaces_list",
|
|
@@ -875,7 +850,7 @@ function registerTinyCloudTools(server, selection, validator) {
|
|
|
875
850
|
// package.json
|
|
876
851
|
var package_default = {
|
|
877
852
|
name: "@tinycloud/mcp",
|
|
878
|
-
version: "0.5.0-beta.
|
|
853
|
+
version: "0.5.0-beta.8",
|
|
879
854
|
description: "TinyCloud delegated operations MCP server",
|
|
880
855
|
keywords: [
|
|
881
856
|
"tinycloud",
|
|
@@ -946,9 +921,9 @@ var package_default = {
|
|
|
946
921
|
dependencies: {
|
|
947
922
|
"@modelcontextprotocol/node": "2.0.0-beta.4",
|
|
948
923
|
"@modelcontextprotocol/server": "2.0.0-beta.4",
|
|
949
|
-
"@tinycloud/node-sdk": "3.2.0-beta.
|
|
924
|
+
"@tinycloud/node-sdk": "3.2.0-beta.7",
|
|
950
925
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
951
|
-
"@tinycloud/operations": "0.5.0-beta.
|
|
926
|
+
"@tinycloud/operations": "0.5.0-beta.8",
|
|
952
927
|
jose: "6.1.3"
|
|
953
928
|
},
|
|
954
929
|
devDependencies: {
|