@trusty-squire/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 +21 -0
- package/README.md +83 -0
- package/dist/api-client.d.ts +130 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +156 -0
- package/dist/api-client.js.map +1 -0
- package/dist/install/agents.d.ts +16 -0
- package/dist/install/agents.d.ts.map +1 -0
- package/dist/install/agents.js +171 -0
- package/dist/install/agents.js.map +1 -0
- package/dist/install/cli.d.ts +24 -0
- package/dist/install/cli.d.ts.map +1 -0
- package/dist/install/cli.js +277 -0
- package/dist/install/cli.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +100 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +25 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +112 -0
- package/dist/session.js.map +1 -0
- package/dist/tools/cancel.d.ts +12 -0
- package/dist/tools/cancel.d.ts.map +1 -0
- package/dist/tools/cancel.js +34 -0
- package/dist/tools/cancel.js.map +1 -0
- package/dist/tools/get-credential.d.ts +15 -0
- package/dist/tools/get-credential.d.ts.map +1 -0
- package/dist/tools/get-credential.js +44 -0
- package/dist/tools/get-credential.js.map +1 -0
- package/dist/tools/get-usage.d.ts +6 -0
- package/dist/tools/get-usage.d.ts.map +1 -0
- package/dist/tools/get-usage.js +24 -0
- package/dist/tools/get-usage.js.map +1 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +48 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-services.d.ts +15 -0
- package/dist/tools/list-services.d.ts.map +1 -0
- package/dist/tools/list-services.js +55 -0
- package/dist/tools/list-services.js.map +1 -0
- package/dist/tools/list-subscriptions.d.ts +6 -0
- package/dist/tools/list-subscriptions.d.ts.map +1 -0
- package/dist/tools/list-subscriptions.js +25 -0
- package/dist/tools/list-subscriptions.js.map +1 -0
- package/dist/tools/provision-any.d.ts +120 -0
- package/dist/tools/provision-any.d.ts.map +1 -0
- package/dist/tools/provision-any.js +243 -0
- package/dist/tools/provision-any.js.map +1 -0
- package/dist/tools/provision.d.ts +30 -0
- package/dist/tools/provision.d.ts.map +1 -0
- package/dist/tools/provision.js +108 -0
- package/dist/tools/provision.js.map +1 -0
- package/dist/tools/rotate-credential.d.ts +12 -0
- package/dist/tools/rotate-credential.d.ts.map +1 -0
- package/dist/tools/rotate-credential.js +43 -0
- package/dist/tools/rotate-credential.js.map +1 -0
- package/dist/tools/wait-for-approval.d.ts +28 -0
- package/dist/tools/wait-for-approval.d.ts.map +1 -0
- package/dist/tools/wait-for-approval.js +82 -0
- package/dist/tools/wait-for-approval.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// provision_any — universal signup bot for any free API service.
|
|
2
|
+
//
|
|
3
|
+
// Runs the browser on the user's machine (their IP, their fingerprint).
|
|
4
|
+
// Aliases + inbox live on Trusty Squire's API so the SES forwarding
|
|
5
|
+
// pipeline (see apps/api/src/routes/ses-webhook.ts) can deliver
|
|
6
|
+
// verification emails.
|
|
7
|
+
//
|
|
8
|
+
// Auth model: Tier 0 machine token from the session file. No account or
|
|
9
|
+
// mandate required for free-tier signups. When the user hits their quota
|
|
10
|
+
// the API returns a structured quota_exceeded response and we tell Claude
|
|
11
|
+
// to surface a pairing CTA.
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { UniversalSignupBot, InboxClient, ProxyLLMClient, detectAsn, } from "@trusty-squire/universal-bot";
|
|
14
|
+
import { openSessionStorage } from "../session.js";
|
|
15
|
+
export const provisionAnyInputSchema = z.object({
|
|
16
|
+
service: z.string().describe("Name of the service to sign up for (e.g., 'Postmark', 'Mailgun')"),
|
|
17
|
+
signup_url: z.string().optional().describe("Direct URL to signup page (optional, will search if not provided)"),
|
|
18
|
+
});
|
|
19
|
+
// JSON Schema for MCP `tools/list`. The SDK forwards `inputSchema` verbatim
|
|
20
|
+
// to the host agent (Claude Code, Goose, etc.); a zod object stringifies to
|
|
21
|
+
// `{}` and leaves the LLM blind to required parameters. The schema here
|
|
22
|
+
// must mirror `provisionAnyInputSchema` above.
|
|
23
|
+
const PROVISION_ANY_JSON_SCHEMA = {
|
|
24
|
+
type: "object",
|
|
25
|
+
required: ["service"],
|
|
26
|
+
properties: {
|
|
27
|
+
service: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Name of the service to sign up for (e.g., 'Postmark', 'Mailgun', 'IPInfo')",
|
|
30
|
+
},
|
|
31
|
+
signup_url: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Direct URL to the service's signup page. Optional — the bot will navigate from the service name if omitted.",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const DESCRIPTION = `Sign up for any free API service automatically using browser automation.
|
|
38
|
+
|
|
39
|
+
WHEN TO CALL THIS TOOL:
|
|
40
|
+
- The user wants an account for a service Trusty Squire doesn't have a native adapter for
|
|
41
|
+
- Call list_services first; if the service ISN'T in the directory, use this tool
|
|
42
|
+
- Best for free-tier developer services with traditional email/password signup
|
|
43
|
+
|
|
44
|
+
BEHAVIOR:
|
|
45
|
+
- Runs Playwright on the user's machine (uses their IP for captcha resilience)
|
|
46
|
+
- Receives verification emails via Trusty Squire's SES inbound infrastructure
|
|
47
|
+
- Returns API credentials directly on success
|
|
48
|
+
- Free for the first N signups per machine (Tier 0); after that, returns a
|
|
49
|
+
pairing CTA the user clicks to upgrade
|
|
50
|
+
|
|
51
|
+
POSSIBLE RESPONSES:
|
|
52
|
+
- status="success" + credentials → use them immediately
|
|
53
|
+
- status="quota_exceeded" + cta_pair_url → tell the user to run \`npx @trusty-squire/mcp pair\`
|
|
54
|
+
or open the URL to upgrade. Their next signup will work.
|
|
55
|
+
- status="captcha_blocked" → the signup site uses captcha we can't bypass. Tell the user
|
|
56
|
+
to sign up manually at the service's signup URL.
|
|
57
|
+
- status="failed" → the form filled but submission didn't yield credentials. Show steps[].`;
|
|
58
|
+
export const provisionAnyTool = {
|
|
59
|
+
name: "provision_any_service",
|
|
60
|
+
description: DESCRIPTION,
|
|
61
|
+
jsonInputSchema: PROVISION_ANY_JSON_SCHEMA,
|
|
62
|
+
inputSchema: provisionAnyInputSchema,
|
|
63
|
+
handler: async (input, _api) => {
|
|
64
|
+
// Pull the machine token from session storage. The install CLI writes it.
|
|
65
|
+
const storage = await openSessionStorage();
|
|
66
|
+
const session = await storage.read();
|
|
67
|
+
if (session === null || session.machine_token === undefined) {
|
|
68
|
+
// Production users hit this when they've never run the install CLI.
|
|
69
|
+
// Local-dev users hit this if their session.json only has an
|
|
70
|
+
// agent_session_token (Tier 1 pair without a machine_token issued).
|
|
71
|
+
// Surface both paths so the agent can route the user correctly.
|
|
72
|
+
const hasPartialSession = session !== null && session.agent_session_token !== undefined;
|
|
73
|
+
return {
|
|
74
|
+
status: "not_installed",
|
|
75
|
+
message: hasPartialSession
|
|
76
|
+
? "This machine is paired (Tier 1) but has no Tier 0 machine_token, which provision_any_service requires. Run `node /home/chode/trusty-squire/apps/mcp/dist/install/cli.js install --target=goose` to issue one, or in production run `npx @trusty-squire/mcp install`."
|
|
77
|
+
: "Trusty Squire isn't installed on this machine. In production run `npx @trusty-squire/mcp install`. For local dev against this repo run `node /home/chode/trusty-squire/apps/mcp/dist/install/cli.js install --target=goose`.",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const apiBase = session.api_base_url;
|
|
81
|
+
// Create an alias through the API (consumes one quota slot for Tier 0).
|
|
82
|
+
const inboxClient = new InboxClient({
|
|
83
|
+
baseUrl: apiBase,
|
|
84
|
+
apiKey: session.machine_token,
|
|
85
|
+
});
|
|
86
|
+
const runId = `mcp-${Date.now().toString(36)}`;
|
|
87
|
+
let alias;
|
|
88
|
+
try {
|
|
89
|
+
alias = await inboxClient.createAlias({
|
|
90
|
+
account_id: session.account_id ?? "anonymous",
|
|
91
|
+
service: input.service,
|
|
92
|
+
run_id: runId,
|
|
93
|
+
});
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.error(`[provision-any] alias=${alias} apiBase=${apiBase}`);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
99
|
+
// The createAlias error body has the structured payload; parse it back.
|
|
100
|
+
if (/quota_exceeded/.test(message)) {
|
|
101
|
+
const match = message.match(/\{.*\}/s);
|
|
102
|
+
if (match !== null) {
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(match[0]);
|
|
105
|
+
return {
|
|
106
|
+
status: "quota_exceeded",
|
|
107
|
+
service: input.service,
|
|
108
|
+
quota_limit: parsed["quota_limit"],
|
|
109
|
+
quota_used: parsed["quota_used"],
|
|
110
|
+
cta_pair_url: parsed["cta_pair_url"],
|
|
111
|
+
message: "You've used your free signups on this machine. " +
|
|
112
|
+
"Run `npx @trusty-squire/mcp pair` (or open the cta_pair_url) to upgrade.",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// fall through
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
status: "error",
|
|
122
|
+
service: input.service,
|
|
123
|
+
error: message,
|
|
124
|
+
message: `Couldn't reach Trusty Squire's API to set up an alias.`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Construct an LLMPair that routes through Trusty Squire's proxy.
|
|
128
|
+
// The user pays nothing for LLM calls — the operator's OpenRouter
|
|
129
|
+
// key handles them server-side, gated by the machine token's
|
|
130
|
+
// rolling rate limit. Cheap mode (Gemini Flash) is the primary;
|
|
131
|
+
// Sonnet is the parse-failure fallback.
|
|
132
|
+
const llmPair = {
|
|
133
|
+
primary: new ProxyLLMClient({
|
|
134
|
+
apiBaseUrl: apiBase,
|
|
135
|
+
machineToken: session.machine_token,
|
|
136
|
+
tier: "cheap",
|
|
137
|
+
}),
|
|
138
|
+
premium: new ProxyLLMClient({
|
|
139
|
+
apiBaseUrl: apiBase,
|
|
140
|
+
machineToken: session.machine_token,
|
|
141
|
+
tier: "premium",
|
|
142
|
+
}),
|
|
143
|
+
};
|
|
144
|
+
// Run the bot locally with this alias. Bot uses the inboxClient to long-
|
|
145
|
+
// poll the API for any verification emails that arrive via SES.
|
|
146
|
+
const bot = new UniversalSignupBot();
|
|
147
|
+
const result = await bot.signup({
|
|
148
|
+
service: input.service,
|
|
149
|
+
...(input.signup_url !== undefined ? { signupUrl: input.signup_url } : {}),
|
|
150
|
+
email: alias,
|
|
151
|
+
inbox: inboxClient,
|
|
152
|
+
llm: llmPair,
|
|
153
|
+
});
|
|
154
|
+
// Best-effort cleanup of the alias once we're done with it. Failure is
|
|
155
|
+
// non-fatal — the alias TTL-expires anyway.
|
|
156
|
+
try {
|
|
157
|
+
await inboxClient.revokeAlias(alias);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// noop
|
|
161
|
+
}
|
|
162
|
+
// If a captcha was encountered (whether or not we got past it),
|
|
163
|
+
// report it to the API for the analytics ledger. The result.captcha
|
|
164
|
+
// field is set by the agent's pre/post-submit/re-plan gates. We do
|
|
165
|
+
// a fresh asn lookup at event time rather than relying on the
|
|
166
|
+
// install-time one — users move networks, and "where was the
|
|
167
|
+
// machine when this happened" is the analytically interesting bit.
|
|
168
|
+
if (result.captcha !== undefined) {
|
|
169
|
+
// Fire-and-forget; we don't want the captcha-event POST to
|
|
170
|
+
// affect what the user sees. Failures here are logged to stderr
|
|
171
|
+
// and otherwise ignored.
|
|
172
|
+
void postCaptchaEvent(apiBase, session.machine_token, {
|
|
173
|
+
service: input.service,
|
|
174
|
+
captcha_kind: result.captcha.kind,
|
|
175
|
+
blocked: result.captcha.blocked,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (result.success && result.credentials !== undefined) {
|
|
179
|
+
return {
|
|
180
|
+
status: "success",
|
|
181
|
+
service: input.service,
|
|
182
|
+
credentials: result.credentials,
|
|
183
|
+
steps: result.steps,
|
|
184
|
+
message: `Successfully signed up for ${input.service}. Credentials are in this response — show them to the user (or save to their .env).`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// Authoritative: if the agent recorded a captcha encounter and
|
|
188
|
+
// marked it blocked, that's a captcha_blocked outcome. Replaces
|
|
189
|
+
// the earlier substring heuristic which had to scan steps[].
|
|
190
|
+
if (result.captcha !== undefined && result.captcha.blocked) {
|
|
191
|
+
return {
|
|
192
|
+
status: "captcha_blocked",
|
|
193
|
+
service: input.service,
|
|
194
|
+
error: result.error ?? "Captcha challenge blocked automated signup.",
|
|
195
|
+
steps: result.steps,
|
|
196
|
+
captcha_kind: result.captcha.kind,
|
|
197
|
+
browser_channel: result.browser_channel ?? null,
|
|
198
|
+
message: `${input.service} blocked automated signup with a ${result.captcha.kind} captcha. ` +
|
|
199
|
+
`Tell the user to sign up manually at ${input.signup_url ?? `https://${input.service.toLowerCase()}.com`}.`,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
status: "failed",
|
|
204
|
+
service: input.service,
|
|
205
|
+
error: result.error ?? "Unknown error",
|
|
206
|
+
steps: result.steps,
|
|
207
|
+
browser_channel: result.browser_channel ?? null,
|
|
208
|
+
message: `Couldn't finish signing up for ${input.service}. Show the user the steps[] for debugging.`,
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
// Best-effort POST to /v1/captcha-events. We don't care about the
|
|
213
|
+
// response — at worst the event is lost, which is no worse than the
|
|
214
|
+
// pre-instrumentation state. Captures fresh asn at event time when
|
|
215
|
+
// possible; the API also falls back to the install-time asn from the
|
|
216
|
+
// MachineToken row if we can't supply one here.
|
|
217
|
+
async function postCaptchaEvent(apiBase, machineToken, event) {
|
|
218
|
+
try {
|
|
219
|
+
const asn = await detectAsn();
|
|
220
|
+
const body = {
|
|
221
|
+
service: event.service,
|
|
222
|
+
captcha_kind: event.captcha_kind,
|
|
223
|
+
blocked: event.blocked,
|
|
224
|
+
...(asn !== null
|
|
225
|
+
? {
|
|
226
|
+
asn: { class: asn.class, org: asn.org, country: asn.country, number: asn.asn },
|
|
227
|
+
}
|
|
228
|
+
: {}),
|
|
229
|
+
};
|
|
230
|
+
await fetch(`${apiBase}/v1/captcha-events`, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: {
|
|
233
|
+
"content-type": "application/json",
|
|
234
|
+
"x-machine-token": machineToken,
|
|
235
|
+
},
|
|
236
|
+
body: JSON.stringify(body),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
console.error(`[provision-any] captcha event report failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=provision-any.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provision-any.js","sourceRoot":"","sources":["../../src/tools/provision-any.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type Tool } from "./index.js";
|
|
3
|
+
declare const inputSchema: z.ZodObject<{
|
|
4
|
+
service: z.ZodString;
|
|
5
|
+
plan: z.ZodDefault<z.ZodString>;
|
|
6
|
+
project_name: z.ZodString;
|
|
7
|
+
category: z.ZodOptional<z.ZodString>;
|
|
8
|
+
cost_cents: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
recurrence: z.ZodDefault<z.ZodEnum<["one_time", "monthly", "yearly", "none"]>>;
|
|
10
|
+
requirements: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
service: string;
|
|
13
|
+
plan: string;
|
|
14
|
+
project_name: string;
|
|
15
|
+
cost_cents: number;
|
|
16
|
+
recurrence: "one_time" | "monthly" | "yearly" | "none";
|
|
17
|
+
category?: string | undefined;
|
|
18
|
+
requirements?: Record<string, unknown> | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
service: string;
|
|
21
|
+
project_name: string;
|
|
22
|
+
plan?: string | undefined;
|
|
23
|
+
category?: string | undefined;
|
|
24
|
+
cost_cents?: number | undefined;
|
|
25
|
+
recurrence?: "one_time" | "monthly" | "yearly" | "none" | undefined;
|
|
26
|
+
requirements?: Record<string, unknown> | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
export declare const provisionTool: Tool<z.infer<typeof inputSchema>>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=provision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provision.d.ts","sourceRoot":"","sources":["../../src/tools/provision.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAgB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAErD,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;EAaf,CAAC;AA8BH,eAAO,MAAM,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAiE3D,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertPaired } from "./index.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
service: z.string().min(1),
|
|
5
|
+
plan: z.string().min(1).default("free"),
|
|
6
|
+
project_name: z.string().min(1).max(120),
|
|
7
|
+
// The runtime needs category + cost for the mandate evaluator;
|
|
8
|
+
// the MCP tool's caller may not know these. When omitted, we
|
|
9
|
+
// request the registry directory for default category.
|
|
10
|
+
category: z.string().optional(),
|
|
11
|
+
cost_cents: z.number().int().nonnegative().default(0),
|
|
12
|
+
recurrence: z
|
|
13
|
+
.enum(["one_time", "monthly", "yearly", "none"])
|
|
14
|
+
.default("none"),
|
|
15
|
+
requirements: z.record(z.string(), z.unknown()).optional(),
|
|
16
|
+
});
|
|
17
|
+
const DESCRIPTION = `Sign up for and configure a SaaS service on the user's behalf, returning
|
|
18
|
+
working credentials. The user has pre-authorized spending within their
|
|
19
|
+
spending policy, so no manual signup, email verification, or card entry
|
|
20
|
+
is needed from them.
|
|
21
|
+
|
|
22
|
+
WHEN TO CALL THIS TOOL:
|
|
23
|
+
- The user's project needs an API key for any external SaaS
|
|
24
|
+
- You would otherwise instruct the user to "go sign up at example.com"
|
|
25
|
+
- You need a service-specific credential the user does not yet have
|
|
26
|
+
- The user mentions needing hosting, a database, email, monitoring,
|
|
27
|
+
auth, AI APIs, payments, analytics, error tracking, or similar
|
|
28
|
+
|
|
29
|
+
DO NOT instruct the user to sign up manually if a Squire adapter exists.
|
|
30
|
+
Call list_services first if you are unsure whether the service is supported.
|
|
31
|
+
|
|
32
|
+
BEHAVIOR:
|
|
33
|
+
- Free tiers are auto-approved within the user's spending policy (silent)
|
|
34
|
+
- Paid tiers above the silent ceiling return status="pending_approval"
|
|
35
|
+
with a one-click approval link the user can click in their terminal
|
|
36
|
+
- Returns within 3-15 seconds for native adapters, up to 60s for browser-based
|
|
37
|
+
- Stores credentials in the user's vault automatically
|
|
38
|
+
- Idempotent on (user, service, project_name)
|
|
39
|
+
|
|
40
|
+
AFTER PROVISIONING:
|
|
41
|
+
- Use get_credential() to retrieve the API key for use in code
|
|
42
|
+
- Reference credentials via env vars; do not paste secrets into source files
|
|
43
|
+
- Tell the user what was set up and the cost, even when silent`;
|
|
44
|
+
export const provisionTool = {
|
|
45
|
+
name: "provision",
|
|
46
|
+
description: DESCRIPTION,
|
|
47
|
+
inputSchema,
|
|
48
|
+
jsonInputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
required: ["service", "project_name"],
|
|
51
|
+
properties: {
|
|
52
|
+
service: { type: "string" },
|
|
53
|
+
plan: { type: "string", default: "free" },
|
|
54
|
+
project_name: { type: "string" },
|
|
55
|
+
category: { type: "string" },
|
|
56
|
+
cost_cents: { type: "integer", minimum: 0, default: 0 },
|
|
57
|
+
recurrence: {
|
|
58
|
+
type: "string",
|
|
59
|
+
enum: ["one_time", "monthly", "yearly", "none"],
|
|
60
|
+
default: "none",
|
|
61
|
+
},
|
|
62
|
+
requirements: { type: "object" },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
async handler(args, api) {
|
|
66
|
+
assertPaired(api);
|
|
67
|
+
// If the caller didn't supply category, look it up in the registry.
|
|
68
|
+
let category = args.category;
|
|
69
|
+
if (category === undefined) {
|
|
70
|
+
const dir = await api.listServices();
|
|
71
|
+
const match = dir.adapters.find((a) => a.service === args.service);
|
|
72
|
+
category = match?.category ?? "unknown";
|
|
73
|
+
}
|
|
74
|
+
const res = await api.createRun({
|
|
75
|
+
service: args.service,
|
|
76
|
+
plan: args.plan,
|
|
77
|
+
project_name: args.project_name,
|
|
78
|
+
category,
|
|
79
|
+
cost_cents: args.cost_cents,
|
|
80
|
+
recurrence: args.recurrence,
|
|
81
|
+
});
|
|
82
|
+
if (res.decision === "silent") {
|
|
83
|
+
return {
|
|
84
|
+
status: "active",
|
|
85
|
+
run_id: res.run.id,
|
|
86
|
+
run_state: res.run.state,
|
|
87
|
+
message: "Sign-up enqueued silently within the user's spending policy.",
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (res.decision === "needs_approval") {
|
|
91
|
+
return {
|
|
92
|
+
status: "pending_approval",
|
|
93
|
+
run_id: res.run.id,
|
|
94
|
+
run_state: res.run.state,
|
|
95
|
+
approval_url: res.approval_url,
|
|
96
|
+
reasons: res.reasons ?? [],
|
|
97
|
+
required_confidence: res.required_confidence ?? "high",
|
|
98
|
+
message: "Approval required. Show the user the approval_url so they can confirm.",
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
status: "rejected",
|
|
103
|
+
run_id: res.run.id,
|
|
104
|
+
message: "The mandate rejected this action.",
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=provision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provision.js","sourceRoot":"","sources":["../../src/tools/provision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { Tool } from "./index.js";
|
|
3
|
+
declare const inputSchema: z.ZodObject<{
|
|
4
|
+
reference: z.ZodString;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
reference: string;
|
|
7
|
+
}, {
|
|
8
|
+
reference: string;
|
|
9
|
+
}>;
|
|
10
|
+
export declare const rotateCredentialTool: Tool<z.infer<typeof inputSchema>>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=rotate-credential.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rotate-credential.d.ts","sourceRoot":"","sources":["../../src/tools/rotate-credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,QAAA,MAAM,WAAW;;;;;;EAEf,CAAC;AAoBH,eAAO,MAAM,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAqBlE,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const inputSchema = z.object({
|
|
3
|
+
reference: z.string().min(1),
|
|
4
|
+
});
|
|
5
|
+
const DESCRIPTION = `Rotate a vault-stored credential (e.g. when the user suspects a leak).
|
|
6
|
+
The adapter's rotate flow generates a fresh credential, stores it in
|
|
7
|
+
the vault under the same reference, and revokes the old value at the
|
|
8
|
+
service.
|
|
9
|
+
|
|
10
|
+
WHEN TO CALL THIS TOOL:
|
|
11
|
+
- The user explicitly asks "rotate the X key"
|
|
12
|
+
- A credential leak has been confirmed
|
|
13
|
+
- Compliance policy requires periodic rotation and the rotation due
|
|
14
|
+
date has passed
|
|
15
|
+
|
|
16
|
+
BEHAVIOR:
|
|
17
|
+
- Default confidence requirement is medium (not high) — rotation is a
|
|
18
|
+
privileged operation but doesn't authorize new spending
|
|
19
|
+
- In v0 the rotate flow is partially wired; the rotate adapter step
|
|
20
|
+
exists but the runtime's flow selector doesn't pick the rotate flow
|
|
21
|
+
yet — see chunk 11+ roadmap. This tool currently returns 202.`;
|
|
22
|
+
export const rotateCredentialTool = {
|
|
23
|
+
name: "rotate_credential",
|
|
24
|
+
description: DESCRIPTION,
|
|
25
|
+
inputSchema,
|
|
26
|
+
jsonInputSchema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
required: ["reference"],
|
|
29
|
+
properties: { reference: { type: "string" } },
|
|
30
|
+
},
|
|
31
|
+
async handler(args, _api) {
|
|
32
|
+
// v0 stub. The endpoint doesn't exist on apps/api yet — the chunk-11
|
|
33
|
+
// spec lists rotate as a future improvement. We return a clear
|
|
34
|
+
// not-implemented body so the coding agent can surface this to
|
|
35
|
+
// the user rather than acting on a false success.
|
|
36
|
+
return {
|
|
37
|
+
status: "not_implemented",
|
|
38
|
+
reference: args.reference,
|
|
39
|
+
message: "Credential rotation is a v0 stub. The cancel + rotate flow selectors arrive in a later chunk. Track progress at /v1/usage.",
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=rotate-credential.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rotate-credential.js","sourceRoot":"","sources":["../../src/tools/rotate-credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC7B,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;gEAgB4C,CAAC;AAEjE,MAAM,CAAC,MAAM,oBAAoB,GAAsC;IACrE,IAAI,EAAE,mBAAmB;IACzB,WAAW,EAAE,WAAW;IACxB,WAAW;IACX,eAAe,EAAE;QACf,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,WAAW,CAAC;QACvB,UAAU,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KAC9C;IACD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI;QACtB,qEAAqE;QACrE,+DAA+D;QAC/D,+DAA+D;QAC/D,kDAAkD;QAClD,OAAO;YACL,MAAM,EAAE,iBAAiB;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EACL,4HAA4H;SAC/H,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ApiClient } from "../api-client.js";
|
|
3
|
+
import { type Tool } from "./index.js";
|
|
4
|
+
declare const inputSchema: z.ZodObject<{
|
|
5
|
+
run_id: z.ZodString;
|
|
6
|
+
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
7
|
+
poll_interval_seconds: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
run_id: string;
|
|
10
|
+
timeout_seconds: number;
|
|
11
|
+
poll_interval_seconds: number;
|
|
12
|
+
}, {
|
|
13
|
+
run_id: string;
|
|
14
|
+
timeout_seconds?: number | undefined;
|
|
15
|
+
poll_interval_seconds?: number | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const waitForApprovalTool: Tool<z.infer<typeof inputSchema>>;
|
|
18
|
+
export declare function waitForApprovalImpl(args: z.infer<typeof inputSchema>, api: ApiClient, options?: {
|
|
19
|
+
sleep?: (ms: number) => Promise<void>;
|
|
20
|
+
now?: () => number;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
status: string;
|
|
23
|
+
run_state: string;
|
|
24
|
+
run_id: string;
|
|
25
|
+
reason?: string;
|
|
26
|
+
}>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=wait-for-approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-approval.d.ts","sourceRoot":"","sources":["../../src/tools/wait-for-approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAgB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAErD,QAAA,MAAM,WAAW;;;;;;;;;;;;EAIf,CAAC;AAcH,eAAO,MAAM,mBAAmB,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAiBjE,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,EACjC,GAAG,EAAE,SAAS,EACd,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAC1E,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBjF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertPaired } from "./index.js";
|
|
3
|
+
const inputSchema = z.object({
|
|
4
|
+
run_id: z.string().min(1),
|
|
5
|
+
timeout_seconds: z.number().int().positive().max(600).default(120),
|
|
6
|
+
poll_interval_seconds: z.number().int().positive().max(30).default(2),
|
|
7
|
+
});
|
|
8
|
+
const DESCRIPTION = `Wait for a run that's in pending_approval to be granted or denied by the user.
|
|
9
|
+
Returns the final status once the run leaves PENDING_APPROVAL or the timeout elapses.
|
|
10
|
+
|
|
11
|
+
WHEN TO CALL THIS TOOL:
|
|
12
|
+
- After provision() returns status="pending_approval"
|
|
13
|
+
- When you want to block until the user clicks the approval link
|
|
14
|
+
|
|
15
|
+
BEHAVIOR:
|
|
16
|
+
- Polls the run every poll_interval_seconds (default 2s)
|
|
17
|
+
- Returns once the run leaves PENDING_APPROVAL or timeout_seconds elapses
|
|
18
|
+
- Does NOT auto-grant; only the user can grant via the approval URL`;
|
|
19
|
+
export const waitForApprovalTool = {
|
|
20
|
+
name: "wait_for_approval",
|
|
21
|
+
description: DESCRIPTION,
|
|
22
|
+
inputSchema,
|
|
23
|
+
jsonInputSchema: {
|
|
24
|
+
type: "object",
|
|
25
|
+
required: ["run_id"],
|
|
26
|
+
properties: {
|
|
27
|
+
run_id: { type: "string" },
|
|
28
|
+
timeout_seconds: { type: "integer", minimum: 1, maximum: 600, default: 120 },
|
|
29
|
+
poll_interval_seconds: { type: "integer", minimum: 1, maximum: 30, default: 2 },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
async handler(args, api) {
|
|
33
|
+
assertPaired(api);
|
|
34
|
+
return waitForApprovalImpl(args, api);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export async function waitForApprovalImpl(args, api, options = {}) {
|
|
38
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
39
|
+
const now = options.now ?? (() => Date.now());
|
|
40
|
+
const deadline = now() + args.timeout_seconds * 1000;
|
|
41
|
+
while (true) {
|
|
42
|
+
const run = await api.getRun(args.run_id);
|
|
43
|
+
if (run.state !== "PENDING_APPROVAL") {
|
|
44
|
+
return {
|
|
45
|
+
status: terminalDescription(run.state),
|
|
46
|
+
run_state: run.state,
|
|
47
|
+
run_id: run.id,
|
|
48
|
+
...(run.failure_reason !== null ? { reason: run.failure_reason } : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (now() >= deadline) {
|
|
52
|
+
return {
|
|
53
|
+
status: "timeout",
|
|
54
|
+
run_state: run.state,
|
|
55
|
+
run_id: run.id,
|
|
56
|
+
reason: "wait_for_approval_timed_out",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
await sleep(args.poll_interval_seconds * 1000);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function defaultSleep(ms) {
|
|
63
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
64
|
+
}
|
|
65
|
+
function terminalDescription(state) {
|
|
66
|
+
switch (state) {
|
|
67
|
+
case "PROVISIONING":
|
|
68
|
+
case "ADAPTER_EXECUTING":
|
|
69
|
+
case "CRED_EXTRACTED":
|
|
70
|
+
case "VAULT_WRITTEN":
|
|
71
|
+
return "granted";
|
|
72
|
+
case "COMPLETE":
|
|
73
|
+
return "active";
|
|
74
|
+
case "REJECTED":
|
|
75
|
+
return "denied";
|
|
76
|
+
case "FAILED":
|
|
77
|
+
return "failed";
|
|
78
|
+
default:
|
|
79
|
+
return state.toLowerCase();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=wait-for-approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-approval.js","sourceRoot":"","sources":["../../src/tools/wait-for-approval.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trusty-squire/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Local MCP server vibe coding agents install. Thin relay to the Trusty Squire API.",
|
|
6
|
+
"main": "./dist/server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"squire-mcp": "./dist/install/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
21
|
+
"open": "^10.1.0",
|
|
22
|
+
"yaml": "^2.6.1",
|
|
23
|
+
"zod": "^3.23.8",
|
|
24
|
+
"@trusty-squire/universal-bot": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"keytar": "^7.9.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@vitest/coverage-v8": "^1.6.1",
|
|
31
|
+
"tsx": "^4.7.0",
|
|
32
|
+
"typescript": "^5.3.3",
|
|
33
|
+
"vitest": "^1.2.0"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"test:cov": "vitest run --coverage",
|
|
40
|
+
"dev:server": "tsx src/server.ts",
|
|
41
|
+
"install:dev": "tsx src/install/cli.ts"
|
|
42
|
+
}
|
|
43
|
+
}
|