@relvo/mcp 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +116 -5
  2. package/bin/relvo-mcp.mjs +1 -1
  3. package/dist/client/agent-run.js +681 -0
  4. package/dist/client/browser.js +650 -0
  5. package/dist/client/evm-wallet.js +214 -0
  6. package/dist/client/execution-context.js +6 -3
  7. package/dist/client/index.js +3 -342
  8. package/dist/client/local-input.js +18 -11
  9. package/dist/client/local-profile.js +22 -110
  10. package/dist/client/local-setup.js +0 -1
  11. package/dist/client/tool-name-hints.js +48 -0
  12. package/dist/client/wallet-access.js +207 -0
  13. package/dist/mcp/agent-server.js +237 -0
  14. package/dist/mcp/context.js +70 -0
  15. package/dist/mcp/host-schema.js +47 -0
  16. package/dist/mcp/instructions.js +17 -0
  17. package/dist/mcp/local-upload.js +12 -4
  18. package/dist/mcp/operations.js +30 -0
  19. package/dist/mcp/runtime.js +25 -5
  20. package/dist/mcp/server.js +293 -69
  21. package/dist/mcp/version.js +1 -0
  22. package/dist/schema/accountRequestMethods.js +1 -0
  23. package/dist/schema/cronValidator.js +150 -0
  24. package/dist/schema/platformApiKey.js +5 -5
  25. package/dist/schema/platformBillingConstants.js +3 -0
  26. package/dist/schema/platformCapabilityFamilies.js +217 -0
  27. package/dist/schema/platformConnectionCatalog.js +20 -3
  28. package/dist/schema/platformConnectionPolicy.js +26 -0
  29. package/dist/schema/platformCustomTools.js +113 -0
  30. package/dist/schema/platformFunding.js +9 -0
  31. package/dist/schema/platformHttpConstraints.js +7 -0
  32. package/dist/schema/platformMcpTools.js +7 -0
  33. package/dist/schema/platformMetaTools.js +10 -6
  34. package/dist/schema/platformMonitorSchedule.js +123 -0
  35. package/dist/schema/platformMonitors.js +159 -0
  36. package/dist/schema/platformOAuth.js +7 -0
  37. package/dist/schema/platformPublicExecution.js +72 -25
  38. package/dist/schema/platformRoutes.js +119 -0
  39. package/dist/schema/platformSkills.js +1 -1
  40. package/dist/schema/platformTeamSecretName.js +3 -0
  41. package/dist/schema/platformTeamSkills.js +49 -0
  42. package/dist/schema/platformToolAccess.js +13 -0
  43. package/dist/schema/platformTriggers.js +152 -0
  44. package/dist/schema/platformWorkflows.js +100 -0
  45. package/dist/schema/scheduleTimeZone.js +15 -0
  46. package/dist/schema/toolDiscovery.js +44 -6
  47. package/dist/schema/toolInputRepair.js +110 -0
  48. package/dist/schema/toolResultContext.js +33 -1
  49. package/package.json +1 -1
  50. package/dist/schema/amazonAds.js +0 -13
  51. package/dist/schema/oauth2.js +0 -64
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ import { projectScopeSchema } from "./platformPublicExecution.js";
3
+ import { PLATFORM_CUSTOM_TOOL_LIMITS, platformVersionAuthorSchema } from "./platformCustomTools.js";
4
+ export const PLATFORM_TEAM_SKILL_LIMITS = {
5
+ perProject: 200,
6
+ versions: 50,
7
+ titleChars: 80,
8
+ descriptionChars: 500,
9
+ contentBytes: 32_768,
10
+ };
11
+ export const PLATFORM_TEAM_SKILL_SLUG = /^[a-z][a-z0-9_]{1,39}$/;
12
+ export const platformTeamSkillSlugSchema = z.string().regex(PLATFORM_TEAM_SKILL_SLUG).describe("Lowercase letters, digits and underscores, starting with a letter.");
13
+ export const platformTeamSkillDefinitionSchema = z.object({
14
+ title: z.string().trim().min(1).max(PLATFORM_TEAM_SKILL_LIMITS.titleChars),
15
+ description: z.string().trim().min(1).max(PLATFORM_TEAM_SKILL_LIMITS.descriptionChars).describe("When an agent should follow this skill; agents choose skills by it."),
16
+ content: z.string().trim().min(1).refine(value => new TextEncoder().encode(value).byteLength <= PLATFORM_TEAM_SKILL_LIMITS.contentBytes, "The content is too long")
17
+ .describe("Markdown: the steps, the tools to use (by name) and what to check. No secrets."),
18
+ }).strict();
19
+ export const platformTeamSkillCreateSchema = platformTeamSkillDefinitionSchema.extend({ slug: platformTeamSkillSlugSchema }).strict();
20
+ export const platformTeamSkillUpdateSchema = platformTeamSkillDefinitionSchema.extend({ expectedVersion: z.number().int().min(1) }).strict();
21
+ export const platformTeamSkillRestoreSchema = z.object({ version: z.number().int().min(1), expectedVersion: z.number().int().min(1) }).strict();
22
+ export const platformTeamSkillSchema = z.object({ slug: platformTeamSkillSlugSchema, title: z.string(), description: z.string(), content: z.string(),
23
+ version: z.number().int().min(1), createdAt: z.string(), updatedAt: z.string() });
24
+ export const platformTeamSkillSummarySchema = platformTeamSkillSchema.omit({ content: true });
25
+ export const platformTeamSkillVersionSchema = z.object({ version: z.number().int().min(1), title: z.string(), createdAt: z.string() });
26
+ export const platformSuggestionSchema = z.discriminatedUnion("kind", [
27
+ z.object({ kind: z.literal("custom_tool"), occurrences: z.number().int().min(2), lastSeenAt: z.string(),
28
+ executions: z.array(z.string()).min(1).max(3),
29
+ code: z.string().min(1).max(PLATFORM_CUSTOM_TOOL_LIMITS.codeChars) }),
30
+ z.object({ kind: z.literal("skill"), occurrences: z.number().int().min(2), lastSeenAt: z.string(), tools: z.array(z.string()).min(2).max(3) }),
31
+ ]);
32
+ export const platformSuggestionsSchema = z.object({ suggestions: z.array(platformSuggestionSchema).max(20), since: z.string() });
33
+ export const platformTeamSkillHistorySchema = platformTeamSkillVersionSchema.extend({ author: platformVersionAuthorSchema });
34
+ export const platformTeamSkillVersionDetailSchema = platformTeamSkillSchema.pick({ title: true, description: true, content: true })
35
+ .extend({ version: z.number().int().min(1), createdAt: z.string(), author: platformVersionAuthorSchema });
36
+ const skillInput = projectScopeSchema.extend({ slug: platformTeamSkillSlugSchema });
37
+ const skillResult = z.object({ skill: platformTeamSkillSchema }).strict();
38
+ export const platformTeamSkillContracts = {
39
+ listDeveloperTeamSkills: { input: projectScopeSchema.strict(), output: z.object({ skills: z.array(platformTeamSkillSummarySchema.extend({ canEdit: z.boolean(), canDelete: z.boolean() })),
40
+ canCreate: z.boolean() }).strict() },
41
+ getDeveloperTeamSkill: { input: skillInput.strict(), output: skillResult },
42
+ createDeveloperTeamSkill: { input: projectScopeSchema.extend({ skill: platformTeamSkillCreateSchema }).strict(), output: skillResult },
43
+ updateDeveloperTeamSkill: { input: skillInput.extend({ skill: platformTeamSkillUpdateSchema }).strict(), output: skillResult },
44
+ restoreDeveloperTeamSkill: { input: skillInput.merge(platformTeamSkillRestoreSchema).strict(), output: skillResult },
45
+ deleteDeveloperTeamSkill: { input: skillInput.strict(), output: z.object({ success: z.literal(true) }).strict() },
46
+ listDeveloperTeamSkillVersions: { input: skillInput.strict(), output: z.object({ versions: z.array(platformTeamSkillHistorySchema) }).strict() },
47
+ getDeveloperTeamSkillVersion: { input: skillInput.extend({ version: z.number().int().min(1) }).strict(), output: z.object({ version: platformTeamSkillVersionDetailSchema }).strict() },
48
+ listDeveloperSuggestions: { input: projectScopeSchema.strict(), output: platformSuggestionsSchema },
49
+ };
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ import { platformCustomToolNameSchema } from "./platformCustomTools.js";
3
+ const connectionId = z.string().regex(/^[A-Za-z0-9-]{1,64}$/);
4
+ export const platformConnectionGrantSchema = z.templateLiteral(["connection:", connectionId]);
5
+ export const platformConnectionOperationGrantSchema = z.templateLiteral(["connection:", connectionId, "/", z.string().regex(/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/)]);
6
+ export const memberToolAccessSchema = z.array(z.union([platformCustomToolNameSchema, platformConnectionGrantSchema, platformConnectionOperationGrantSchema])).max(20_000).nullable();
7
+ export const keyToolAccessSchema = memberToolAccessSchema;
8
+ export const platformTeamAccessSchema = z.object({
9
+ members: z.array(z.object({ memberId: z.string(), email: z.string(), name: z.string().nullable(), role: z.string(), toolAccess: memberToolAccessSchema }).loose()),
10
+ invitations: z.array(z.object({ id: z.string(), email: z.string(), role: z.string(), expiresAt: z.string(), toolAccess: memberToolAccessSchema }).loose()),
11
+ });
12
+ export const platformMemberAccessUpdateSchema = z.object({ toolAccess: memberToolAccessSchema }).strict();
13
+ export const memberTeamSkillAccessSchema = z.enum(["EDIT", "OWN", "READ"]);
@@ -0,0 +1,152 @@
1
+ import { z } from "zod";
2
+ import { projectScopeSchema, platformJsonObjectSchema, platformMicrosSchema } from "./platformPublicExecution.js";
3
+ import { platformMonitorDestinationInputSchema, platformMonitorDestinationSchema, platformMonitorRecipientSchema, PLATFORM_MONITOR_DESTINATION_LIMITS } from "./platformMonitors.js";
4
+ import { platformWorkflowSlugSchema } from "./platformWorkflows.js";
5
+ import { PLATFORM_FREE_TOOL_OVERAGE_MICROS } from "./platformBillingConstants.js";
6
+ export const PLATFORM_TRIGGER_LIMITS = {
7
+ perProject: 50,
8
+ perTeam: 100,
9
+ nameChars: 80,
10
+ eventsPage: 100,
11
+ eventDataBytes: 65_536,
12
+ ingressBodyBytes: 1_048_576,
13
+ retentionDays: 30,
14
+ keptEvents: 10_000,
15
+ eventsPerBatch: 100,
16
+ typesPage: 100,
17
+ };
18
+ export const PLATFORM_TRIGGER_INTERVALS = { "1m": 60, "5m": 300, "15m": 900, "1h": 3_600 };
19
+ export const platformTriggerIntervalSchema = z.enum(["1m", "5m", "15m", "1h"]);
20
+ export const PLATFORM_TRIGGER_DEFAULT_MAXIMUM_CHARGE_MICROS_PER_POLL = PLATFORM_FREE_TOOL_OVERAGE_MICROS;
21
+ export const PLATFORM_TRIGGER_ID = /^trg_[A-Za-z0-9_-]{1,100}$/;
22
+ export const platformTriggerIdSchema = z.string().regex(PLATFORM_TRIGGER_ID);
23
+ export const PLATFORM_TRIGGER_TYPE = /^[a-z0-9][a-z0-9_]{0,63}\.[a-z0-9][a-z0-9_.]{0,79}$/;
24
+ export const platformTriggerTypeIdSchema = z.string().regex(PLATFORM_TRIGGER_TYPE);
25
+ const toolName = z.string().regex(/^[a-z][a-z0-9_]{1,127}$/);
26
+ const connectionId = z.string().min(1).max(128);
27
+ const pointer = z.string().max(200).regex(/^(\/[^/]*)*$/);
28
+ export const platformTriggerDestinationKindSchema = z.enum(["webhook", "slack", "discord", "email", "workflow"]);
29
+ const workflowDestination = z.object({
30
+ kind: z.literal("workflow"),
31
+ label: z.string().trim().max(PLATFORM_MONITOR_DESTINATION_LIMITS.labelChars).default(""),
32
+ workflow: platformWorkflowSlugSchema,
33
+ input: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}$/).default("event").describe("The workflow input that receives the event object."),
34
+ maximumChargeMicros: platformMicrosSchema,
35
+ }).strict();
36
+ export const platformTriggerDestinationInputSchema = z.union([platformMonitorDestinationInputSchema, workflowDestination]);
37
+ export const platformTriggerDestinationSchema = platformMonitorDestinationSchema.extend({
38
+ kind: platformTriggerDestinationKindSchema,
39
+ workflow: z.object({ slug: platformWorkflowSlugSchema, input: z.string(), maximumChargeMicros: platformMicrosSchema }).nullable().default(null),
40
+ });
41
+ const common = {
42
+ name: z.string().trim().max(PLATFORM_TRIGGER_LIMITS.nameChars).default("").describe("Empty names the trigger after its type or operation."),
43
+ connectionId: connectionId.describe("The connection of the app, from /tools/connections."),
44
+ destinations: z.array(platformTriggerDestinationInputSchema).max(PLATFORM_MONITOR_DESTINATION_LIMITS.perMonitor).default([]),
45
+ };
46
+ export const platformTriggerCreateSchema = z.discriminatedUnion("kind", [
47
+ z.object({
48
+ kind: z.literal("poll"),
49
+ tool: toolName.describe("A read operation of the connected app, for example github__issues_list_for_repo."),
50
+ arguments: platformJsonObjectSchema.default({}).describe("The operation's arguments without connectionId."),
51
+ interval: platformTriggerIntervalSchema.default("5m"),
52
+ itemsPath: pointer.optional().describe("Where the list of items is in the response body. Omit to use the largest list."),
53
+ maximumChargeMicrosPerPoll: platformMicrosSchema.default(PLATFORM_TRIGGER_DEFAULT_MAXIMUM_CHARGE_MICROS_PER_POLL),
54
+ ...common,
55
+ }).strict(),
56
+ z.object({
57
+ kind: z.literal("webhook"),
58
+ type: platformTriggerTypeIdSchema.describe("A trigger type from /trigger-types, for example github.push."),
59
+ config: platformJsonObjectSchema.default({}).describe("The type's settings, valid against its configSchema."),
60
+ ...common,
61
+ }).strict(),
62
+ ]);
63
+ export const platformTriggerUpdateSchema = z.object({
64
+ enabled: z.boolean().optional(),
65
+ name: z.string().trim().max(PLATFORM_TRIGGER_LIMITS.nameChars).optional().describe("Empty or omitted keeps the name."),
66
+ }).strict();
67
+ export const platformTriggerStatusSchema = z.enum(["active", "disabled", "failed"]);
68
+ export const platformTriggerSchema = z.object({
69
+ id: platformTriggerIdSchema,
70
+ name: z.string(),
71
+ kind: z.enum(["poll", "webhook"]),
72
+ app: z.string(),
73
+ connectionId: z.string(),
74
+ status: platformTriggerStatusSchema,
75
+ statusReason: z.string().nullable(),
76
+ tool: z.string().nullable(),
77
+ arguments: platformJsonObjectSchema.nullable(),
78
+ interval: platformTriggerIntervalSchema.nullable(),
79
+ itemsPath: z.string().nullable(),
80
+ maximumChargeMicrosPerPoll: platformMicrosSchema.nullable(),
81
+ type: z.string().nullable(),
82
+ config: platformJsonObjectSchema.nullable(),
83
+ apiKeyId: z.string(),
84
+ lastCheckedAt: z.string().nullable(),
85
+ lastEventAt: z.string().nullable(),
86
+ nextCheckAt: z.string().nullable(),
87
+ lastError: z.string().nullable(),
88
+ createdAt: z.string(),
89
+ destinations: z.array(platformTriggerDestinationSchema),
90
+ });
91
+ export const platformTriggerEventSchema = z.object({
92
+ id: z.string().regex(/^\d{1,20}$/),
93
+ triggerId: platformTriggerIdSchema,
94
+ type: z.string(),
95
+ key: z.string(),
96
+ title: z.string(),
97
+ url: z.string(),
98
+ data: platformJsonObjectSchema,
99
+ receivedAt: z.string(),
100
+ });
101
+ export const platformTriggerEventsQuerySchema = z.object({
102
+ after: z.string().regex(/^\d{1,20}$/).optional(),
103
+ limit: z.coerce.number().int().min(1).max(PLATFORM_TRIGGER_LIMITS.eventsPage).default(50),
104
+ }).strict();
105
+ export const platformTriggerEventsSchema = z.object({ events: z.array(platformTriggerEventSchema), nextAfter: z.string().nullable() });
106
+ export const platformTriggerTypeSchema = z.object({
107
+ type: platformTriggerTypeIdSchema,
108
+ app: z.string(),
109
+ name: z.string(),
110
+ description: z.string(),
111
+ configSchema: platformJsonObjectSchema,
112
+ setup: z.enum(["automatic", "app"]),
113
+ available: z.boolean(),
114
+ unavailableReason: z.string().nullable(),
115
+ });
116
+ export const platformTriggerOperationSchema = z.object({ tool: toolName, app: z.string(), description: z.string(), inputSchema: platformJsonObjectSchema });
117
+ export const platformTriggerTypesQuerySchema = z.object({
118
+ app: z.string().regex(/^[a-z0-9][a-z0-9_]{0,63}$/),
119
+ connectionId: z.string().max(128).optional(),
120
+ query: z.string().trim().max(100).default(""),
121
+ offset: z.coerce.number().int().min(0).max(100_000).default(0),
122
+ limit: z.coerce.number().int().min(1).max(PLATFORM_TRIGGER_LIMITS.typesPage).default(50),
123
+ }).strict();
124
+ export const platformTriggerTypesSchema = z.object({
125
+ types: z.array(platformTriggerTypeSchema),
126
+ operations: z.array(platformTriggerOperationSchema),
127
+ total: z.number().int().min(0),
128
+ });
129
+ export const platformTriggerDeliverySchema = z.object({
130
+ version: z.literal(1),
131
+ type: z.literal("trigger.event"),
132
+ deliveryId: z.string(),
133
+ trigger: z.object({ id: platformTriggerIdSchema, name: z.string(), kind: z.enum(["poll", "webhook"]), app: z.string(), source: z.string(), connectionId: z.string() }).strict(),
134
+ event: platformTriggerEventSchema.omit({ triggerId: true }),
135
+ }).strict();
136
+ const projectTrigger = projectScopeSchema.extend({ triggerId: platformTriggerIdSchema });
137
+ export const platformTriggerContracts = {
138
+ listDeveloperTriggers: { input: projectScopeSchema.strict(), output: z.object({ triggers: z.array(platformTriggerSchema) }).strict() },
139
+ createDeveloperTrigger: { input: projectScopeSchema.extend({ apiKeyId: z.string().min(1).max(128), trigger: platformTriggerCreateSchema }).strict(),
140
+ output: z.object({ trigger: platformTriggerSchema }).strict() },
141
+ updateDeveloperTrigger: { input: projectTrigger.merge(platformTriggerUpdateSchema).strict(), output: z.object({ trigger: platformTriggerSchema }).strict() },
142
+ deleteDeveloperTrigger: { input: projectTrigger.strict(), output: z.object({ success: z.literal(true) }).strict() },
143
+ listDeveloperTriggerEvents: {
144
+ input: projectScopeSchema.extend({ triggerId: platformTriggerIdSchema.optional(), after: platformTriggerEventsQuerySchema.shape.after,
145
+ limit: z.number().int().min(1).max(PLATFORM_TRIGGER_LIMITS.eventsPage).default(50) }).strict(),
146
+ output: platformTriggerEventsSchema,
147
+ },
148
+ listDeveloperTriggerTypes: { input: projectScopeSchema.merge(platformTriggerTypesQuerySchema).strict(), output: platformTriggerTypesSchema },
149
+ addDeveloperTriggerDestination: { input: projectTrigger.extend({ destination: platformTriggerDestinationInputSchema }).strict(), output: z.object({ trigger: platformTriggerSchema }).strict() },
150
+ removeDeveloperTriggerDestination: { input: projectTrigger.extend({ destinationId: platformTriggerDestinationSchema.shape.id }).strict(), output: z.object({ trigger: platformTriggerSchema }).strict() },
151
+ listDeveloperTriggerRecipients: { input: projectScopeSchema.strict(), output: z.object({ recipients: z.array(platformMonitorRecipientSchema) }).strict() },
152
+ };
@@ -0,0 +1,100 @@
1
+ import { z } from "zod";
2
+ import { platformExecutionIdSchema, platformJsonObjectSchema, platformMicrosSchema, platformProviderChoiceSchema, platformToolNameSchema } from "./platformPublicExecution.js";
3
+ import { platformCustomToolInputSchemaSchema } from "./platformCustomTools.js";
4
+ export const PLATFORM_WORKFLOW_LIMITS = {
5
+ perProject: 50,
6
+ steps: 20,
7
+ versions: 50,
8
+ activeRunsPerProject: 10,
9
+ titleChars: 80,
10
+ descriptionChars: 1_000,
11
+ stepsBytes: 32_768,
12
+ inputBytes: 16_384,
13
+ };
14
+ export const PLATFORM_WORKFLOW_SLUG = /^[a-z][a-z0-9_]{1,39}$/;
15
+ export const platformWorkflowSlugSchema = z.string().regex(PLATFORM_WORKFLOW_SLUG).describe("Lowercase letters, digits and underscores, starting with a letter.");
16
+ export const platformWorkflowStepIdSchema = z.string().regex(/^[a-z][a-z0-9_]{0,39}$/);
17
+ export const platformWorkflowRunIdSchema = z.string().regex(/^wfr_[A-Za-z0-9_-]{1,100}$/);
18
+ const pointerSchema = z.string().max(500).regex(/^(?:\/(?:[^~/]|~[01])*)*$/, "A JSON Pointer such as /data/companies/0/name");
19
+ const referenceSchema = z.union([z.object({ $input: z.string(), path: z.string().optional() }).strict(), z.object({ $step: z.string(), path: z.string().optional() }).strict()]);
20
+ export function platformWorkflowReference(value) {
21
+ const parsed = referenceSchema.safeParse(value);
22
+ if (!parsed.success)
23
+ return undefined;
24
+ const path = parsed.data.path ?? "";
25
+ return "$input" in parsed.data ? { kind: "input", name: parsed.data.$input, path } : { kind: "step", step: parsed.data.$step, path };
26
+ }
27
+ export function platformWorkflowReferences(value) {
28
+ const reference = platformWorkflowReference(value);
29
+ if (reference)
30
+ return [reference];
31
+ if (Array.isArray(value))
32
+ return value.flatMap(platformWorkflowReferences);
33
+ if (value !== null && typeof value === "object")
34
+ return Object.values(value).flatMap(platformWorkflowReferences);
35
+ return [];
36
+ }
37
+ export const platformWorkflowStepSchema = z.object({
38
+ id: platformWorkflowStepIdSchema.describe("Names the step for later steps' references: lowercase letters, digits and underscores."),
39
+ tool: platformToolNameSchema.describe("A tool of the project's catalog."),
40
+ arguments: platformJsonObjectSchema.describe("The tool's arguments. Write {\"$input\":\"name\"} for a run input or {\"$step\":\"id\",\"path\":\"/data/0/url\"} for a value of an earlier step's output."),
41
+ provider: platformProviderChoiceSchema,
42
+ }).strict();
43
+ const byteLength = (value) => new TextEncoder().encode(JSON.stringify(value)).byteLength;
44
+ const inputNames = (inputSchema) => {
45
+ const properties = inputSchema.properties;
46
+ return new Set(properties !== null && typeof properties === "object" && !Array.isArray(properties) ? Object.keys(properties) : []);
47
+ };
48
+ const definitionShape = {
49
+ title: z.string().trim().min(1).max(PLATFORM_WORKFLOW_LIMITS.titleChars),
50
+ description: z.string().trim().min(1).max(PLATFORM_WORKFLOW_LIMITS.descriptionChars).describe("What the workflow does and returns; agents choose workflows by it."),
51
+ inputSchema: platformCustomToolInputSchemaSchema.describe("JSON Schema of the run inputs, e.g. {\"type\":\"object\",\"properties\":{\"domain\":{\"type\":\"string\"}},\"required\":[\"domain\"]}."),
52
+ steps: z.array(platformWorkflowStepSchema).min(1).max(PLATFORM_WORKFLOW_LIMITS.steps).describe("Tool calls in the order they run; each starts after the previous one succeeded."),
53
+ };
54
+ function checkDefinition(definition, context) {
55
+ if (byteLength(definition.steps) > PLATFORM_WORKFLOW_LIMITS.stepsBytes)
56
+ context.addIssue({ code: "custom", path: ["steps"], message: "The steps are too large" });
57
+ const inputs = inputNames(definition.inputSchema), earlier = new Set();
58
+ definition.steps.forEach((step, index) => {
59
+ if (earlier.has(step.id))
60
+ context.addIssue({ code: "custom", path: ["steps", index, "id"], message: "Each step id is used once" });
61
+ for (const reference of platformWorkflowReferences(step.arguments)) {
62
+ if (!pointerSchema.safeParse(reference.path).success)
63
+ context.addIssue({ code: "custom", path: ["steps", index, "arguments"], message: `Invalid path ${reference.path}` });
64
+ else if (reference.kind === "input" && !inputs.has(reference.name))
65
+ context.addIssue({ code: "custom", path: ["steps", index, "arguments"], message: `No input named ${reference.name}` });
66
+ else if (reference.kind === "step" && !earlier.has(reference.step))
67
+ context.addIssue({ code: "custom", path: ["steps", index, "arguments"], message: `No earlier step named ${reference.step}` });
68
+ }
69
+ earlier.add(step.id);
70
+ });
71
+ }
72
+ export const platformWorkflowDefinitionSchema = z.object(definitionShape).strict().superRefine(checkDefinition);
73
+ export const platformWorkflowCreateSchema = z.object({ ...definitionShape, slug: platformWorkflowSlugSchema }).strict().superRefine(checkDefinition);
74
+ export const platformWorkflowUpdateSchema = z.object({ ...definitionShape, expectedVersion: z.number().int().min(1) }).strict().superRefine(checkDefinition);
75
+ export const platformWorkflowSchema = z.object({ slug: platformWorkflowSlugSchema, title: z.string(), description: z.string(), inputSchema: platformJsonObjectSchema,
76
+ steps: z.array(platformWorkflowStepSchema), version: z.number().int().min(1), createdAt: z.string(), updatedAt: z.string() });
77
+ export const platformWorkflowSummarySchema = platformWorkflowSchema.omit({ inputSchema: true, steps: true });
78
+ export const platformWorkflowVersionSchema = z.object({ version: z.number().int().min(1), title: z.string(), createdAt: z.string() });
79
+ export const platformWorkflowRunCreateSchema = z.object({
80
+ inputs: platformJsonObjectSchema.default({}).refine(value => byteLength(value) <= PLATFORM_WORKFLOW_LIMITS.inputBytes, "The inputs are too large")
81
+ .describe("Values for the workflow's input schema."),
82
+ maximumChargeMicros: platformMicrosSchema.describe("The most the whole run may charge, in micros of USD. A step whose price no longer fits stops the run before it is bought."),
83
+ version: z.number().int().min(1).optional().describe("Run an earlier kept version; omit for the current one."),
84
+ }).strict();
85
+ export const platformWorkflowRunStatusSchema = z.enum(["running", "succeeded", "failed", "needs_review", "cancelled"]);
86
+ export const platformWorkflowRunStepSchema = z.object({
87
+ id: platformWorkflowStepIdSchema, tool: platformToolNameSchema,
88
+ status: z.enum(["pending", "queued", "running", "waiting", "succeeded", "failed", "cancelled", "needs_review"]),
89
+ executionId: platformExecutionIdSchema.nullable(), chargedMicros: platformMicrosSchema, errorCode: z.string().nullable(),
90
+ }).strict();
91
+ export const platformWorkflowRunSchema = z.object({
92
+ id: platformWorkflowRunIdSchema, workflow: platformWorkflowSlugSchema, version: z.number().int().min(1), status: platformWorkflowRunStatusSchema,
93
+ cancelRequested: z.boolean(), errorCode: z.string().nullable(), inputs: platformJsonObjectSchema,
94
+ maximumChargeMicros: platformMicrosSchema, chargedMicros: platformMicrosSchema, steps: z.array(platformWorkflowRunStepSchema),
95
+ resultExecutionId: platformExecutionIdSchema.nullable(),
96
+ createdAt: z.string(), updatedAt: z.string(), completedAt: z.string().nullable(),
97
+ }).strict();
98
+ export const platformWorkflowRunSummarySchema = platformWorkflowRunSchema.omit({ inputs: true, steps: true });
99
+ export const platformWorkflowRunListQuerySchema = z.object({ before: platformWorkflowRunIdSchema.optional(), limit: z.coerce.number().int().min(1).max(50).default(20) }).strict();
100
+ export const platformWorkflowRunListSchema = z.object({ runs: z.array(platformWorkflowRunSummarySchema), nextBefore: platformWorkflowRunIdSchema.nullable() }).strict();
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ export function isScheduleTimeZone(value) {
3
+ if (!/^[A-Za-z][A-Za-z0-9_+-]*(\/[A-Za-z0-9_+-]+)*$/.test(value))
4
+ return false;
5
+ try {
6
+ new Intl.DateTimeFormat("en-US", { timeZone: value });
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ export const ScheduleTimeZone = z.string().max(64)
14
+ .refine(isScheduleTimeZone, "Invalid time zone (must be an IANA tz-database name).")
15
+ .transform(value => new Intl.DateTimeFormat("en-US", { timeZone: value }).resolvedOptions().timeZone);
@@ -1,26 +1,64 @@
1
1
  import { z } from "zod";
2
- import { platformCatalogSchema } from "./platformPublicExecution.js";
2
+ import { platformCatalogSchema, platformMicrosSchema, platformQuoteSchema, platformRequestIntentSchema, platformToolInputSchema, platformToolNameSchema, validatePlatformArgumentIdentifiers } from "./platformPublicExecution.js";
3
3
  export const MAX_DISCOVERED_TOOLS = 16;
4
4
  export const SINGLE_QUERY_TOOL_LIMIT = 12;
5
5
  export const TOOL_DISCOVERY_CONTEXT_MAX_BYTES = 24_000;
6
6
  export const TOOL_DISCOVERY_REQUEST_TIMEOUT_MS = 60_000;
7
+ export const TOOL_DISCOVERY_FILTER_MAX_VALUES = 32;
8
+ export const TOOL_DISCOVERY_APP_MAX_CHARS = 120;
9
+ const scopeFilterSchema = z.object({
10
+ tools: z.array(platformToolNameSchema).max(TOOL_DISCOVERY_FILTER_MAX_VALUES).default([]),
11
+ providers: z.array(z.string().regex(/^[a-z][a-z0-9_]{1,63}$/)).max(TOOL_DISCOVERY_FILTER_MAX_VALUES).default([]),
12
+ categories: z.array(platformCatalogSchema.shape.tools.element.shape.category).max(TOOL_DISCOVERY_FILTER_MAX_VALUES).default([]),
13
+ }).strict();
7
14
  export const toolDiscoveryInputSchema = z.object({
8
- query: z.string().trim().min(1).max(512).optional().describe("Describe needed capabilities in natural language, or give an exact tool/model name. Omit account names, record IDs and private content."),
9
- category: platformCatalogSchema.shape.tools.element.shape.category.optional(),
10
- provider: z.string().regex(/^[a-z][a-z0-9_]{1,63}$/).optional().describe("Filter provider metadata; does not select an execution provider."),
15
+ intent: platformRequestIntentSchema,
16
+ query: z.string().trim().max(512).transform(value => value || undefined).optional()
17
+ .describe("Short English words for one capability (e.g. list open pull requests), preserving the requested action and resource, or an exact tool/model name. Put the connected service in app. Empty means browse. Omit private content."),
18
+ app: z.string().trim().max(TOOL_DISCOVERY_APP_MAX_CHARS).transform(value => value || undefined).optional()
19
+ .describe("Connected app name/ID, e.g. GitHub: search its indexed API operations only. Unknown/ambiguous or unindexed apps return explicitly unscoped results. Omit or use empty for public-data targets and known tool IDs. Discovery only; does not pin execution."),
20
+ category: z.union([z.literal("all"), z.literal(""), platformCatalogSchema.shape.tools.element.shape.category])
21
+ .transform(value => value === "all" || value === "" ? undefined : value).optional().describe("all or empty means any category."),
22
+ provider: z.string().trim().regex(/^(?:[a-z][a-z0-9_]{1,63})?$/).transform(value => value || undefined).optional()
23
+ .describe("Filter provider metadata; empty means any provider. Does not pin execution."),
24
+ maxChargeMicros: platformMicrosSchema.nullable().optional()
25
+ .describe("Max quoted USD micros per call (1 USD=1000000). 0=free; null/omitted=no cap. Excludes unknown/external costs. Requote actual execution."),
26
+ quoteArguments: platformToolInputSchema.shape.arguments.nullable().optional()
27
+ .describe("Arguments to quote each candidate; null/omitted uses its catalog example. Never include secrets."),
28
+ funding: z.enum(["all", "relvo", "provider", "mixed"]).transform(value => value === "all" ? undefined : value).optional()
29
+ .describe("Filter who bills the call; all/omitted means any. Provider/mixed can incur external charges."),
30
+ priceUnit: z.string().trim().max(100).transform(value => value === "all" || value === "" ? undefined : value).optional()
31
+ .describe("Exact catalog price unit, e.g. request or second; all/empty means any. Unit price is not a call quote."),
32
+ sortBy: z.enum(["relevance", "price_asc"]).optional().describe("Relevance by default; price_asc uses call quotes, with unknown prices last."),
33
+ include: scopeFilterSchema.nullable().optional().describe("Match exact tools/providers/categories. OR within each list, AND across lists; empty/null means any. Discovery only."),
34
+ exclude: scopeFilterSchema.nullable().optional().describe("Remove exact tools, categories, or any tool listing an excluded provider. Exclusion wins; empty/null excludes nothing. Does not pin execution."),
11
35
  availableOnly: z.boolean().default(true),
12
- summary: z.boolean().default(true).describe("Omit schemas and examples; inspect one selected tool when needed."),
36
+ summary: z.boolean().default(true).describe("Summaries for comparing candidates. false requests schemas/examples; oversized definitions are omitted and remain inspectable."),
13
37
  limit: z.number().int().min(1).max(SINGLE_QUERY_TOOL_LIMIT).default(8),
14
38
  mode: z.enum(["auto", "keyword", "semantic"]).default("auto"),
15
- }).strict();
39
+ }).strict().superRefine((value, context) => {
40
+ if (value.quoteArguments == null)
41
+ return;
42
+ const named = value.include?.tools.length ? value.include.tools.length === 1 ? value.include.tools[0] : undefined
43
+ : platformToolNameSchema.safeParse(value.query).data;
44
+ validatePlatformArgumentIdentifiers(named, value.quoteArguments, context, "quoteArguments");
45
+ });
16
46
  const toolSummary = platformCatalogSchema.shape.tools.element.omit({ inputSchema: true, example: true }).extend({
47
+ discoveryQuote: platformQuoteSchema.extend({ basis: z.enum(["arguments", "example"]) }).optional(),
17
48
  inputSchema: platformCatalogSchema.shape.tools.element.shape.inputSchema.optional(),
18
49
  example: platformCatalogSchema.shape.tools.element.shape.example.optional(),
19
50
  models: z.array(platformCatalogSchema.shape.tools.element.shape.models.unwrap().element.omit({ example: true }).extend({
20
51
  example: platformCatalogSchema.shape.tools.element.shape.example.optional(),
21
52
  })).optional(),
22
53
  });
54
+ export const PLATFORM_CONNECTABLE_APPS_MAX = 3;
55
+ export const platformConnectableAppsSchema = z.object({
56
+ instruction: z.string(),
57
+ apps: z.array(z.object({ app: z.string(), name: z.string(), status: z.enum(["not_connected", "reauth_required"]), connectUrl: z.url() }))
58
+ .min(1).max(PLATFORM_CONNECTABLE_APPS_MAX),
59
+ });
23
60
  export const toolDiscoveryResultSchema = platformCatalogSchema.extend({ tools: z.array(toolSummary),
24
61
  discovery: z.object({ mode: z.enum(["browse", "keyword", "semantic"]), candidateCount: z.number().int().nonnegative(), returnedCount: z.number().int().nonnegative(),
25
62
  truncated: z.boolean(), warning: z.string().optional() }),
63
+ connectableApps: platformConnectableAppsSchema.optional(),
26
64
  });
@@ -0,0 +1,110 @@
1
+ import { z } from "zod";
2
+ const isNode = (value) => typeof value === "object" && value !== null;
3
+ const OMITTED = Symbol("omitted");
4
+ const jsonStructure = (text) => {
5
+ if (!/^\s*[[{]/.test(text))
6
+ return undefined;
7
+ try {
8
+ return JSON.parse(text);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ };
14
+ function evidentValue(issue, sent) {
15
+ if (sent === null || sent === "")
16
+ return OMITTED;
17
+ if (issue.code === "invalid_type") {
18
+ if (typeof sent === "string" && issue.expected === "boolean")
19
+ return sent.toLowerCase() === "true" ? true : sent.toLowerCase() === "false" ? false : undefined;
20
+ if (typeof sent === "number" && issue.expected === "boolean")
21
+ return sent === 1 ? true : sent === 0 ? false : undefined;
22
+ if (typeof sent === "string" && (issue.expected === "number" || issue.expected === "int"))
23
+ return /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/.test(sent) ? Number(sent) : undefined;
24
+ const structure = typeof sent === "string" ? jsonStructure(sent) : undefined;
25
+ if (issue.expected === "array" && Array.isArray(structure))
26
+ return structure;
27
+ if ((issue.expected === "object" || issue.expected === "record") && isNode(structure) && !Array.isArray(structure))
28
+ return structure;
29
+ if (issue.expected === "array" && structure === undefined && ["string", "number", "boolean"].includes(typeof sent) && !String(sent).includes(","))
30
+ return [sent];
31
+ if (issue.expected === "array" && isNode(sent) && !Array.isArray(sent))
32
+ return [sent];
33
+ }
34
+ if (issue.code === "invalid_format" && (issue.format === "url" || issue.format === "regex" && /^\^https\??:\/\//.test((issue.pattern ?? "").replace(/^\//, "").replaceAll("\\/", "/")))
35
+ && typeof sent === "string" && /^[a-z0-9-]+(?:\.[a-z0-9-]+)+(?::\d+)?(?:[/?#]\S*)?$/i.test(sent))
36
+ return `https://${sent}`;
37
+ if (issue.code === "invalid_value" && typeof sent === "string") {
38
+ const options = issue.values.filter(value => ["string", "number", "boolean"].includes(typeof value));
39
+ for (const form of [(text) => text.toLowerCase(), (text) => text.toLowerCase().replace(/[\s_-]/g, "")]) {
40
+ const matches = options.filter(value => form(String(value)) === form(sent));
41
+ if (matches.length)
42
+ return matches.length === 1 ? matches[0] : undefined;
43
+ }
44
+ }
45
+ return undefined;
46
+ }
47
+ const valueAt = (root, path) => path.reduce((node, step) => isNode(node) && typeof step !== "symbol" ? node[String(step)] : undefined, root);
48
+ function repairableIssues(issues, input) {
49
+ return issues.flatMap(issue => issue.code !== "invalid_union" ? [issue] : issue.errors
50
+ .map(option => repairableIssues(option.map(inner => ({ ...inner, path: [...issue.path, ...inner.path] })), input))
51
+ .find(option => option.length > 0 && option.every(inner => evidentValue(inner, valueAt(input, inner.path)) !== undefined)) ?? []);
52
+ }
53
+ function repaired(input, issues) {
54
+ const target = structuredClone(input), changes = [];
55
+ for (const issue of issues) {
56
+ const last = issue.path.at(-1), node = valueAt(target, issue.path.slice(0, -1));
57
+ if (last === undefined || typeof last === "symbol" || !isNode(node))
58
+ continue;
59
+ const key = String(last), sent = node[key], value = evidentValue(issue, sent);
60
+ if (value === undefined || value === OMITTED && Array.isArray(node))
61
+ continue;
62
+ if (value === OMITTED)
63
+ delete node[key];
64
+ else
65
+ node[key] = value;
66
+ changes.push({ path: issue.path, node, key, sent });
67
+ }
68
+ return { target, changes };
69
+ }
70
+ function unionOptions(schema) {
71
+ const source = schema instanceof z.ZodPipe ? schema.in : schema;
72
+ return source instanceof z.ZodUnion ? source.options.filter((option) => option instanceof z.ZodType) : [];
73
+ }
74
+ function repairPass(schema, input) {
75
+ const parsed = schema.safeParse(input);
76
+ if (parsed.success)
77
+ return input;
78
+ const { target, changes } = repaired(input, repairableIssues(parsed.error.issues, input));
79
+ const checked = schema.safeParse(target);
80
+ if (checked.success)
81
+ return target;
82
+ for (const option of unionOptions(schema)) {
83
+ const repairedOption = withEvidentValues(option, input);
84
+ if (repairedOption !== input && schema.safeParse(repairedOption).success)
85
+ return repairedOption;
86
+ }
87
+ const evident = (issue) => {
88
+ const inner = repairableIssues([issue], target);
89
+ return inner.length > 0 && inner.every(item => evidentValue(item, valueAt(target, item.path)) !== undefined);
90
+ };
91
+ const kept = changes.filter(change => {
92
+ const refused = checked.error.issues.some(issue => change.path.every((step, index) => issue.path[index] === step)
93
+ && (issue.path.length <= change.path.length || !evident(issue)));
94
+ if (refused)
95
+ change.node[change.key] = change.sent;
96
+ return !refused;
97
+ });
98
+ return kept.length ? target : input;
99
+ }
100
+ const REPAIR_PASSES = 32;
101
+ export function withEvidentValues(schema, input) {
102
+ let current = input;
103
+ for (let pass = 0; pass < REPAIR_PASSES; pass++) {
104
+ const next = repairPass(schema, current);
105
+ if (next === current)
106
+ break;
107
+ current = next;
108
+ }
109
+ return current;
110
+ }
@@ -1,12 +1,15 @@
1
1
  import { z } from "zod";
2
2
  export const TOOL_RESULT_CONTEXT_MAX_BYTES = 24_000;
3
- export const TOOL_RESULT_PAGE_MAX_ITEMS = 100;
3
+ export const TOOL_RESULT_PAGE_MAX_ITEMS = 1_000;
4
4
  export const TOOL_RESULT_PAGE_MAX_CHARS = 8_000;
5
+ export const TOOL_RESULT_PAGE_MAX_FIELDS = 16;
5
6
  export const toolResultSelectionSchema = z.object({
6
7
  path: z.array(z.string().max(256)).max(16).default([]).describe("Object keys / array indices, starting at the stored output. Example: [\"results\", \"0\", \"data\"]."),
7
8
  offset: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).default(0),
8
9
  limit: z.number().int().min(1).max(TOOL_RESULT_PAGE_MAX_ITEMS).default(20),
9
10
  maxChars: z.number().int().min(1).max(TOOL_RESULT_PAGE_MAX_CHARS).default(4_000),
11
+ fields: z.array(z.string().max(256)).max(TOOL_RESULT_PAGE_MAX_FIELDS).optional()
12
+ .describe("Top-level keys to keep from one object or each object in an array page. Object offsets page the selected keys. Empty keeps every field. Full stored data is unchanged."),
10
13
  }).strict();
11
14
  export const toolResultContextSchema = z.object({
12
15
  offloaded: z.boolean(), totalBytes: z.number().int().nonnegative(), preview: z.json(),
@@ -17,3 +20,32 @@ export const toolResultPageSchema = z.object({
17
20
  offset: z.number().int().nonnegative(), total: z.number().int().nonnegative(), nextOffset: z.number().int().nonnegative().nullable(),
18
21
  context: toolResultContextSchema,
19
22
  });
23
+ export function fitToolResultPage(page, fits) {
24
+ if (page.context.offloaded || fits(page))
25
+ return page;
26
+ const original = page.context.preview;
27
+ const entries = original !== null && typeof original === "object" && !Array.isArray(original) ? Object.entries(original) : null;
28
+ const length = typeof original === "string" || Array.isArray(original) ? original.length : entries?.length ?? 0;
29
+ if (!length)
30
+ return page;
31
+ const prefix = (count) => {
32
+ let end = count;
33
+ if (typeof original === "string" && end < original.length && /[\uD800-\uDBFF]/.test(original[end - 1] ?? "") && /[\uDC00-\uDFFF]/.test(original[end] ?? ""))
34
+ end++;
35
+ const preview = typeof original === "string" || Array.isArray(original) ? original.slice(0, end) : Object.fromEntries(entries?.slice(0, end) ?? []);
36
+ const next = page.offset + end;
37
+ return { ...page, nextOffset: next < page.total ? next : null,
38
+ context: { ...page.context, totalBytes: new TextEncoder().encode(JSON.stringify(preview)).byteLength, preview } };
39
+ };
40
+ let lower = 1, upper = length, best;
41
+ while (lower <= upper) {
42
+ const count = Math.floor((lower + upper) / 2), candidate = prefix(count);
43
+ if (fits(candidate)) {
44
+ best = candidate;
45
+ lower = count + 1;
46
+ }
47
+ else
48
+ upper = count - 1;
49
+ }
50
+ return best ?? page;
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relvo/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Relvo stdio MCP server for research, SEO, social data and media generation.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,13 +0,0 @@
1
- import { z } from "zod";
2
- export const amazonAdsRegionSchema = z.enum(["na", "eu", "fe"]);
3
- export const amazonAdsProfileIdSchema = z.string().regex(/^[1-9][0-9]{0,18}$/)
4
- .pipe(z.string().refine(value => BigInt(value) <= 9223372036854775807n));
5
- export const amazonAdsProfileResourceIdSchema = z.string().regex(/^(?:na|eu|fe):[1-9][0-9]{0,18}$/)
6
- .pipe(z.string().refine(value => BigInt(value.slice(3)) <= 9223372036854775807n));
7
- export function amazonAdsProfileResourceId(region, profileId) {
8
- return amazonAdsProfileResourceIdSchema.parse(`${region}:${profileId}`);
9
- }
10
- export function parseAmazonAdsProfileResourceId(input) {
11
- const [region, profileId] = amazonAdsProfileResourceIdSchema.parse(input).split(":");
12
- return z.object({ region: amazonAdsRegionSchema, profileId: amazonAdsProfileIdSchema }).parse({ region, profileId });
13
- }