@tinycloud/mcp 0.5.5-beta.0 → 0.6.0-beta.2
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 +63 -9
- package/dist/cli.cjs +116 -13
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +116 -13
- package/dist/cli.js.map +1 -1
- package/dist/http-cli.cjs +157 -26
- package/dist/http-cli.cjs.map +1 -1
- package/dist/http-cli.js +157 -26
- package/dist/http-cli.js.map +1 -1
- package/dist/http.cjs +157 -26
- package/dist/http.cjs.map +1 -1
- package/dist/http.js +157 -26
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +162 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +162 -27
- package/dist/index.js.map +1 -1
- package/dist/results.cjs +25 -3
- package/dist/results.cjs.map +1 -1
- package/dist/results.d.cts +9 -6
- package/dist/results.d.ts +9 -6
- package/dist/results.js +25 -3
- package/dist/results.js.map +1 -1
- package/dist/server.cjs +116 -13
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +116 -13
- package/dist/server.js.map +1 -1
- package/dist/tools.cjs +119 -12
- package/dist/tools.cjs.map +1 -1
- package/dist/tools.d.cts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +119 -12
- package/dist/tools.js.map +1 -1
- package/generated/mcp-facts.json +68 -0
- package/package.json +2 -2
- package/skills/tinycloud-delegated-secrets/SKILL.md +4 -0
package/dist/http.cjs
CHANGED
|
@@ -103,6 +103,17 @@ function firstString(...values) {
|
|
|
103
103
|
return values.find((value) => typeof value === "string" && value.length > 0);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// src/logging.ts
|
|
107
|
+
function logToolCall(tool, code, startedAt, replayedGrants) {
|
|
108
|
+
process.stderr.write(`${JSON.stringify({
|
|
109
|
+
tool,
|
|
110
|
+
code,
|
|
111
|
+
durationMs: Math.max(0, Math.round(performance.now() - startedAt)),
|
|
112
|
+
replayedGrants
|
|
113
|
+
})}
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
|
|
106
117
|
// src/remote-store.ts
|
|
107
118
|
var import_node_crypto = require("crypto");
|
|
108
119
|
var import_promises = require("fs/promises");
|
|
@@ -110,6 +121,7 @@ var import_node_path = require("path");
|
|
|
110
121
|
var import_node_sdk2 = require("@tinycloud/node-sdk");
|
|
111
122
|
var import_node_sdk_wasm = require("@tinycloud/node-sdk-wasm");
|
|
112
123
|
var import_operations = require("@tinycloud/operations");
|
|
124
|
+
var import_tc_apps = require("@tinycloud/operations/tc-apps");
|
|
113
125
|
var PROFILE = "agent";
|
|
114
126
|
var MAX_CALLBACK_BYTES = 256 * 1024;
|
|
115
127
|
var LEGACY_DEFAULT_NODE_HOST = "https://node.tinycloud.xyz";
|
|
@@ -162,6 +174,10 @@ var RemoteTenantStore = class {
|
|
|
162
174
|
tenantStateRoot(subject) {
|
|
163
175
|
return (0, import_node_path.join)(this.#config.stateDir, "tenants", this.#tenantId(subject));
|
|
164
176
|
}
|
|
177
|
+
/** Prepare only this authenticated account's profile before its first operation. */
|
|
178
|
+
async initializeTenant(subject) {
|
|
179
|
+
return (await this.#ensureTenant(subject)).stateRoot;
|
|
180
|
+
}
|
|
165
181
|
async connectStatus(subject, ownerDids = []) {
|
|
166
182
|
const tenant = await this.#ensureTenant(subject);
|
|
167
183
|
if (await this.#hasValidSession(tenant, ownerDids)) {
|
|
@@ -176,7 +192,23 @@ var RemoteTenantStore = class {
|
|
|
176
192
|
};
|
|
177
193
|
}
|
|
178
194
|
async decorateAuthorityResult(subject, ownerDids, result) {
|
|
179
|
-
if (!isRecord(result)
|
|
195
|
+
if (!isRecord(result)) return result;
|
|
196
|
+
if (result.status === "error" && isRecord(result.error) && (result.error.code === "SESSION_NOT_FOUND" || result.error.code === "AUTH_REQUIRED")) {
|
|
197
|
+
await this.#ensureTenant(subject);
|
|
198
|
+
const pending2 = await this.#createPending(subject, ownerDids, "bootstrap", BOOTSTRAP_PERMISSIONS);
|
|
199
|
+
return {
|
|
200
|
+
...result,
|
|
201
|
+
error: {
|
|
202
|
+
...result.error,
|
|
203
|
+
details: {
|
|
204
|
+
...isRecord(result.error.details) ? result.error.details : {},
|
|
205
|
+
approvalUrl: this.#connectUrl(pending2),
|
|
206
|
+
expiresAt: pending2.expiresAt
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (result.status !== "authority_required") return result;
|
|
180
212
|
const request = isRecord(result.request) ? result.request : void 0;
|
|
181
213
|
const requestId = typeof request?.requestId === "string" ? request.requestId : void 0;
|
|
182
214
|
const requested = Array.isArray(request?.requested) ? request.requested.filter(isPermissionEntry) : [];
|
|
@@ -189,7 +221,7 @@ var RemoteTenantStore = class {
|
|
|
189
221
|
kind: "openkey",
|
|
190
222
|
requestId,
|
|
191
223
|
url: this.#connectUrl(pending),
|
|
192
|
-
fallback: "Open the approval URL, then retry the same MCP tool."
|
|
224
|
+
fallback: (0, import_tc_apps.tcAppsApprovalMessage)(requested) ?? "Open the approval URL, then retry the same MCP tool."
|
|
193
225
|
}
|
|
194
226
|
};
|
|
195
227
|
}
|
|
@@ -206,7 +238,7 @@ var RemoteTenantStore = class {
|
|
|
206
238
|
permissions: Buffer.from(JSON.stringify({
|
|
207
239
|
permissions: pending.permissions,
|
|
208
240
|
reason: [
|
|
209
|
-
pending.kind === "bootstrap" ? "Connect this TinyCloud account to the hosted MCP delegate." : "Approve the exact capabilities requested by the TinyCloud MCP operation.",
|
|
241
|
+
pending.kind === "bootstrap" ? "Connect this TinyCloud account to the hosted MCP delegate." : (0, import_tc_apps.tcAppsApprovalReason)(pending.permissions) ?? "Approve the exact capabilities requested by the TinyCloud MCP operation.",
|
|
210
242
|
// Owner DIDs come from the verified OpenKey access token of the MCP
|
|
211
243
|
// client that created this link, never from the link itself.
|
|
212
244
|
...pending.ownerDids.length === 0 ? [] : [`Requested by TinyCloud account ${pending.ownerDids.join(", ")}; approve only if that is you.`]
|
|
@@ -634,15 +666,37 @@ var import_ajv = require("@modelcontextprotocol/server/validators/ajv");
|
|
|
634
666
|
var import_operations2 = require("@tinycloud/operations");
|
|
635
667
|
|
|
636
668
|
// src/results.ts
|
|
637
|
-
function toMcpToolResult(result, bindings = []) {
|
|
638
|
-
|
|
669
|
+
function toMcpToolResult(result, bindings = [], options = {}) {
|
|
670
|
+
let canonical = isCanonicalOperationResult(result) ? withRecoveryTool(result, bindings) : {
|
|
639
671
|
status: "error",
|
|
640
672
|
operation: {},
|
|
641
673
|
context: {},
|
|
642
674
|
error: { code: "INTERNAL_ERROR" }
|
|
643
675
|
};
|
|
676
|
+
let serialized;
|
|
677
|
+
if (options.surface === "hosted") {
|
|
678
|
+
serialized = JSON.stringify(canonical);
|
|
679
|
+
if (options.readOnly === true && serialized.length > 1e5) {
|
|
680
|
+
const sql = options.operationId?.startsWith("tinycloud.sql.") || options.operationId === "tinycloud.apps.query";
|
|
681
|
+
canonical = {
|
|
682
|
+
status: "error",
|
|
683
|
+
operation: canonical.operation,
|
|
684
|
+
context: canonical.context,
|
|
685
|
+
error: {
|
|
686
|
+
code: sql ? "SQL_RESULT_LIMIT_EXCEEDED" : "KV_RESPONSE_TOO_LARGE",
|
|
687
|
+
message: sql ? "The result exceeds 100,000 characters. Select fewer columns or rows, add a WHERE filter or a smaller LIMIT, or aggregate the data." : "The result exceeds 100,000 characters. Request fewer keys, narrow the prefix, or use a smaller page size.",
|
|
688
|
+
retryable: false
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
serialized = JSON.stringify(canonical);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
644
694
|
return {
|
|
645
|
-
content: [{
|
|
695
|
+
content: [{
|
|
696
|
+
type: "text",
|
|
697
|
+
text: resultSummary(canonical.status) + (serialized !== void 0 && !(options.operationId === "tinycloud.secrets.get" && canonical.status === "ok") ? `
|
|
698
|
+
${serialized}` : "")
|
|
699
|
+
}],
|
|
646
700
|
structuredContent: canonical
|
|
647
701
|
};
|
|
648
702
|
}
|
|
@@ -691,6 +745,9 @@ var packageRequire = (0, import_node_module.createRequire)(
|
|
|
691
745
|
var generatedCatalog = packageRequire(
|
|
692
746
|
"@tinycloud/operations/operations.json"
|
|
693
747
|
);
|
|
748
|
+
var stableErrorCodes = Object.fromEntries(
|
|
749
|
+
generatedCatalog.stableErrors.map((code) => [code, true])
|
|
750
|
+
);
|
|
694
751
|
var ALL_SURFACES = ["local", "hosted"];
|
|
695
752
|
function bindingSurfaces(binding) {
|
|
696
753
|
return binding.surfaces ?? ALL_SURFACES;
|
|
@@ -854,6 +911,46 @@ var TOOL_BINDINGS = [
|
|
|
854
911
|
// Hosted has no persistent reauthorization path for a revoked session
|
|
855
912
|
// yet (TC-896), so status is local/stdio only.
|
|
856
913
|
surfaces: ["local"]
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
name: "tinycloud_apps_context",
|
|
917
|
+
operationId: "tinycloud.apps.context",
|
|
918
|
+
operationVersion: 1,
|
|
919
|
+
readOnlyHint: true,
|
|
920
|
+
idempotentHint: true,
|
|
921
|
+
destructiveHint: false,
|
|
922
|
+
openWorldHint: true,
|
|
923
|
+
surfaces: ["hosted"]
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
name: "tinycloud_apps_read",
|
|
927
|
+
operationId: "tinycloud.apps.read",
|
|
928
|
+
operationVersion: 1,
|
|
929
|
+
readOnlyHint: true,
|
|
930
|
+
idempotentHint: true,
|
|
931
|
+
destructiveHint: false,
|
|
932
|
+
openWorldHint: true,
|
|
933
|
+
surfaces: ["hosted"]
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
name: "tinycloud_apps_write",
|
|
937
|
+
operationId: "tinycloud.apps.write",
|
|
938
|
+
operationVersion: 1,
|
|
939
|
+
readOnlyHint: false,
|
|
940
|
+
idempotentHint: false,
|
|
941
|
+
destructiveHint: true,
|
|
942
|
+
openWorldHint: true,
|
|
943
|
+
surfaces: ["hosted"]
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
name: "tinycloud_apps_query",
|
|
947
|
+
operationId: "tinycloud.apps.query",
|
|
948
|
+
operationVersion: 1,
|
|
949
|
+
readOnlyHint: true,
|
|
950
|
+
idempotentHint: true,
|
|
951
|
+
destructiveHint: false,
|
|
952
|
+
openWorldHint: true,
|
|
953
|
+
surfaces: ["hosted"]
|
|
857
954
|
}
|
|
858
955
|
];
|
|
859
956
|
function createJsonSchemaValidator() {
|
|
@@ -898,23 +995,50 @@ function registerTinyCloudTools(server, selection, validator) {
|
|
|
898
995
|
annotations
|
|
899
996
|
},
|
|
900
997
|
async (input) => {
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
998
|
+
const startedAt = performance.now();
|
|
999
|
+
const metrics = { replayedGrants: 0 };
|
|
1000
|
+
let code = "INTERNAL_ERROR";
|
|
1001
|
+
try {
|
|
1002
|
+
const result = await (0, import_operations2.invokeOperation)(
|
|
1003
|
+
binding.operationId,
|
|
1004
|
+
binding.operationVersion,
|
|
1005
|
+
invocationTargetForMcp(selection),
|
|
1006
|
+
input,
|
|
1007
|
+
metrics
|
|
1008
|
+
);
|
|
1009
|
+
const projected = selection.transformOperationResult === void 0 ? result : await selection.transformOperationResult(result);
|
|
1010
|
+
const response = toMcpToolResult(projected, TOOL_BINDINGS, {
|
|
1011
|
+
surface: selection.surface,
|
|
1012
|
+
readOnly: binding.readOnlyHint,
|
|
1013
|
+
operationId: binding.operationId
|
|
1014
|
+
});
|
|
1015
|
+
code = outcomeCode(response.structuredContent);
|
|
1016
|
+
return response;
|
|
1017
|
+
} finally {
|
|
1018
|
+
if (selection.surface === "hosted") {
|
|
1019
|
+
logToolCall(binding.name, code, startedAt, metrics.replayedGrants);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
909
1022
|
}
|
|
910
1023
|
);
|
|
911
1024
|
}
|
|
912
1025
|
}
|
|
1026
|
+
function outcomeCode(result) {
|
|
1027
|
+
if (result.status === "error") {
|
|
1028
|
+
const error = result.error;
|
|
1029
|
+
return typeof error?.code === "string" && Object.hasOwn(stableErrorCodes, error.code) ? error.code : "INTERNAL_ERROR";
|
|
1030
|
+
}
|
|
1031
|
+
if (result.status !== "ok") return result.status;
|
|
1032
|
+
const output = result.output;
|
|
1033
|
+
if (output?.outcome === "COPY_PARTIAL") return "COPY_PARTIAL";
|
|
1034
|
+
if (Array.isArray(output?.ops) && output.ops.some((op) => op.outcome !== "ok")) return "PARTIAL";
|
|
1035
|
+
return "ok";
|
|
1036
|
+
}
|
|
913
1037
|
|
|
914
1038
|
// package.json
|
|
915
1039
|
var package_default = {
|
|
916
1040
|
name: "@tinycloud/mcp",
|
|
917
|
-
version: "0.
|
|
1041
|
+
version: "0.6.0-beta.2",
|
|
918
1042
|
description: "TinyCloud delegated operations MCP server",
|
|
919
1043
|
keywords: [
|
|
920
1044
|
"tinycloud",
|
|
@@ -987,7 +1111,7 @@ var package_default = {
|
|
|
987
1111
|
"@modelcontextprotocol/server": "2.0.0-beta.4",
|
|
988
1112
|
"@tinycloud/node-sdk": "3.2.5-beta.0",
|
|
989
1113
|
"@tinycloud/node-sdk-wasm": "1.7.7",
|
|
990
|
-
"@tinycloud/operations": "0.
|
|
1114
|
+
"@tinycloud/operations": "0.6.0-beta.2",
|
|
991
1115
|
jose: "6.1.3"
|
|
992
1116
|
},
|
|
993
1117
|
devDependencies: {
|
|
@@ -1031,13 +1155,13 @@ function createHostedMcpApp(config) {
|
|
|
1031
1155
|
const handler = (0, import_server4.createMcpHandler)(async ({ authInfo: authInfo2 }) => {
|
|
1032
1156
|
const identity = oauthIdentity(authInfo2);
|
|
1033
1157
|
const { subject, ownerDids } = identity;
|
|
1034
|
-
const stateRoot = store.
|
|
1158
|
+
const stateRoot = await store.initializeTenant(subject);
|
|
1035
1159
|
const validator = createJsonSchemaValidator();
|
|
1036
1160
|
const server = new import_server4.McpServer(
|
|
1037
1161
|
{ name: MCP_SERVER_NAME, version: MCP_VERSION },
|
|
1038
1162
|
{
|
|
1039
1163
|
jsonSchemaValidator: validator,
|
|
1040
|
-
instructions: "Call tinycloud_connect first. If an operation returns authority_required, open approval.url, then retry that operation unchanged."
|
|
1164
|
+
instructions: "Call tinycloud_connect first. If an operation returns authority_required, open approval.url, then retry that operation unchanged. For personal records such as weight, workouts, goals or todos, call tinycloud_apps_context first."
|
|
1041
1165
|
}
|
|
1042
1166
|
);
|
|
1043
1167
|
registerTinyCloudTools(server, {
|
|
@@ -1149,14 +1273,21 @@ function registerConnectTool(server, validator, store, identity) {
|
|
|
1149
1273
|
}
|
|
1150
1274
|
},
|
|
1151
1275
|
async () => {
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1276
|
+
const startedAt = performance.now();
|
|
1277
|
+
let code = "INTERNAL_ERROR";
|
|
1278
|
+
try {
|
|
1279
|
+
const status = await store.connectStatus(identity.subject, identity.ownerDids);
|
|
1280
|
+
code = status.connected ? "ok" : "authority_required";
|
|
1281
|
+
return {
|
|
1282
|
+
content: [{
|
|
1283
|
+
type: "text",
|
|
1284
|
+
text: status.connected ? "TinyCloud is connected." : `TinyCloud approval is required. Open ${status.approvalUrl}, then call this tool again. This link expires at ${status.expiresAt}.`
|
|
1285
|
+
}],
|
|
1286
|
+
structuredContent: status
|
|
1287
|
+
};
|
|
1288
|
+
} finally {
|
|
1289
|
+
logToolCall("tinycloud_connect", code, startedAt, 0);
|
|
1290
|
+
}
|
|
1160
1291
|
}
|
|
1161
1292
|
);
|
|
1162
1293
|
}
|