@rubytech/create-maxy-code 0.1.312 → 0.1.313

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 (67) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  3. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  4. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +37 -3
  5. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  6. package/payload/platform/plugins/docs/references/quickbooks.md +29 -0
  7. package/payload/platform/plugins/quickbooks/.claude-plugin/plugin.json +21 -0
  8. package/payload/platform/plugins/quickbooks/PLUGIN.md +91 -0
  9. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +159 -0
  10. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/package.json +3 -0
  11. package/payload/platform/plugins/quickbooks/mcp/dist/index.d.ts +2 -0
  12. package/payload/platform/plugins/quickbooks/mcp/dist/index.d.ts.map +1 -0
  13. package/payload/platform/plugins/quickbooks/mcp/dist/index.js +218 -0
  14. package/payload/platform/plugins/quickbooks/mcp/dist/index.js.map +1 -0
  15. package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.d.ts +37 -0
  16. package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.d.ts.map +1 -0
  17. package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.js +75 -0
  18. package/payload/platform/plugins/quickbooks/mcp/dist/lib/oauth.js.map +1 -0
  19. package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.d.ts +39 -0
  20. package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.d.ts.map +1 -0
  21. package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.js +178 -0
  22. package/payload/platform/plugins/quickbooks/mcp/dist/lib/qbo-client.js.map +1 -0
  23. package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.d.ts +36 -0
  24. package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.d.ts.map +1 -0
  25. package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.js +107 -0
  26. package/payload/platform/plugins/quickbooks/mcp/dist/lib/secrets.js.map +1 -0
  27. package/payload/platform/plugins/quickbooks/mcp/package.json +19 -0
  28. package/payload/platform/plugins/quickbooks/skills/quickbooks/SKILL.md +37 -0
  29. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  30. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +16 -0
  31. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  32. package/payload/server/{chunk-J6WDAWFJ.js → chunk-Q7EWROVN.js} +4 -0
  33. package/payload/server/maxy-edge.js +1 -1
  34. package/payload/server/public/assets/{AccessGate-Dh5A79FU.js → AccessGate-Byskkf47.js} +1 -1
  35. package/payload/server/public/assets/{AdminLoginScreens-B8s2TbT7.js → AdminLoginScreens-DgwQi3HN.js} +1 -1
  36. package/payload/server/public/assets/AdminShell-DQfL2c_L.js +1 -0
  37. package/payload/server/public/assets/{Checkbox-B_sOAyv1.js → Checkbox-C54JSreC.js} +1 -1
  38. package/payload/server/public/assets/OperatorConversations-DulQyF8c.js +1 -0
  39. package/payload/server/public/assets/{admin-gZEndvJT.js → admin-B25rkI9Q.js} +1 -1
  40. package/payload/server/public/assets/{audio-attachment-mime-B0b89VnH.js → audio-attachment-mime-BlwrgPQa.js} +1 -1
  41. package/payload/server/public/assets/{brand-BcNavK6q.css → brand-TWflEl22.css} +1 -1
  42. package/payload/server/public/assets/{browser-BUlB5_MD.js → browser-C9Z5dLlN.js} +1 -1
  43. package/payload/server/public/assets/chat-CGNWDmid.js +1 -0
  44. package/payload/server/public/assets/data-DCQDh0-Y.js +1 -0
  45. package/payload/server/public/assets/{graph-labels-AZLGoVy8.js → graph-labels-BBtL--_M.js} +1 -1
  46. package/payload/server/public/assets/{graph-DMCKpW6P.js → graph-sMzOI71g.js} +2 -2
  47. package/payload/server/public/assets/operator-hI6kJi0I.js +1 -0
  48. package/payload/server/public/assets/page-DbsqlpuX.js +1 -0
  49. package/payload/server/public/assets/{public-G_-UI9nB.js → public-Cs5N4-Az.js} +1 -1
  50. package/payload/server/public/assets/{public-next-C8_nExQB.js → public-next-DoGPYX6T.js} +1 -1
  51. package/payload/server/public/assets/{useSelectionMode-Bf6Rt-sl.js → useSelectionMode-Cs-vtuKI.js} +1 -1
  52. package/payload/server/public/browser.html +6 -6
  53. package/payload/server/public/chat.html +8 -8
  54. package/payload/server/public/data.html +5 -5
  55. package/payload/server/public/graph.html +8 -8
  56. package/payload/server/public/index.html +8 -8
  57. package/payload/server/public/operator.html +10 -10
  58. package/payload/server/public/public-next.html +9 -9
  59. package/payload/server/public/public.html +7 -7
  60. package/payload/server/server.js +600 -491
  61. package/payload/server/public/assets/AdminShell-Cu1zlb6L.js +0 -1
  62. package/payload/server/public/assets/OperatorConversations-hm0Gpu_Q.js +0 -1
  63. package/payload/server/public/assets/chat-BnceaVl7.js +0 -1
  64. package/payload/server/public/assets/data-3Nl3P5wt.js +0 -1
  65. package/payload/server/public/assets/operator-BF4F7k23.js +0 -1
  66. package/payload/server/public/assets/page-DQUEst1o.js +0 -1
  67. /package/payload/server/public/assets/{brand-BQ_u9UdS.js → brand-CYjs10m-.js} +0 -0
@@ -0,0 +1,218 @@
1
+ import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
2
+ initStderrTee("quickbooks");
3
+ import { randomBytes } from "node:crypto";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import { readStore, setCredentials, removeConnection, writePending, } from "./lib/secrets.js";
8
+ import { buildAuthorizeUrl } from "./lib/oauth.js";
9
+ import { qboQuery, qboCreate, qboUpdate, qboReport, auditConnection } from "./lib/qbo-client.js";
10
+ // Plugin-system boot tolerance — see Task 202. Module-init must never throw;
11
+ // the plugin-system enumerates tools/list with no env, so per-account context
12
+ // is resolved per call and a missing account refuses, never crashes.
13
+ const ACCOUNT_ID = process.env.ACCOUNT_ID ?? null;
14
+ const PLATFORM_ROOT = process.env.PLATFORM_ROOT ?? null;
15
+ process.stderr.write(`[quickbooks] boot accountId=${ACCOUNT_ID ?? "null"} platformRoot=${PLATFORM_ROOT ?? "null"}\n`);
16
+ const CONSENT_TTL_MS = 15 * 60 * 1000;
17
+ function refuseNoAccount(toolName) {
18
+ process.stderr.write(`[quickbooks] tool=${toolName} refuse reason=no-account-context\n`);
19
+ return {
20
+ content: [
21
+ {
22
+ type: "text",
23
+ text: `${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID/PLATFORM_ROOT (plugin-system metadata-only mode). Per-account access requires both at spawn.`,
24
+ },
25
+ ],
26
+ isError: true,
27
+ };
28
+ }
29
+ function ok(data) {
30
+ return {
31
+ content: [
32
+ { type: "text", text: typeof data === "string" ? data : JSON.stringify(data, null, 2) },
33
+ ],
34
+ };
35
+ }
36
+ function fail(message) {
37
+ return { content: [{ type: "text", text: message }], isError: true };
38
+ }
39
+ const server = new McpServer({ name: "quickbooks", version: "0.1.0" });
40
+ // Arbitrary QBO resource body (the caller assembles it per Intuit's entity
41
+ // schema; the connector does not model every field).
42
+ const resourceBody = z.record(z.unknown());
43
+ // ---------------------------------------------------------------------------
44
+ // Connection management
45
+ // ---------------------------------------------------------------------------
46
+ server.tool("quickbooks-credentials-set", "Store the operator's own Intuit app credentials (client ID + secret) for this account. Get these by registering an app at developer.intuit.com — Maxy is never the OAuth app owner. environment is 'sandbox' for an Intuit sandbox company or 'production' for real books.", {
47
+ clientId: z.string().describe("Intuit app client ID"),
48
+ clientSecret: z.string().describe("Intuit app client secret"),
49
+ environment: z.enum(["sandbox", "production"]).describe("Which QBO environment these credentials target"),
50
+ }, async ({ clientId, clientSecret, environment }) => {
51
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
52
+ return refuseNoAccount("quickbooks-credentials-set");
53
+ try {
54
+ setCredentials(PLATFORM_ROOT, ACCOUNT_ID, { clientId, clientSecret, environment });
55
+ return ok(`Stored Intuit ${environment} credentials. Next: run quickbooks-authorize-url and have a human authorize each company.`);
56
+ }
57
+ catch (err) {
58
+ return fail(`quickbooks-credentials-set failed: ${err.message}`);
59
+ }
60
+ });
61
+ server.tool("quickbooks-authorize-url", "Generate the Intuit consent URL for a human to click. Returns a URL — hand it to a person; they sign in and authorize one company. Intuit then redirects to the callback, which stores the refresh token. redirectUri MUST exactly match the redirect URI registered in the Intuit app (the brand's public host + /api/quickbooks/callback).", {
62
+ redirectUri: z.string().describe("The exact redirect URI registered in the Intuit app, e.g. https://<public-host>/api/quickbooks/callback"),
63
+ realmHint: z.string().optional().describe("Optional human label for which company is being connected (recorded for correlation only)"),
64
+ }, async ({ redirectUri, realmHint }) => {
65
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
66
+ return refuseNoAccount("quickbooks-authorize-url");
67
+ try {
68
+ const store = readStore(PLATFORM_ROOT, ACCOUNT_ID);
69
+ if (!store)
70
+ return fail("No credentials set. Run quickbooks-credentials-set first.");
71
+ const state = randomBytes(24).toString("base64url");
72
+ const now = Date.now();
73
+ writePending(PLATFORM_ROOT, ACCOUNT_ID, {
74
+ state,
75
+ redirectUri,
76
+ realmHint,
77
+ createdAt: now,
78
+ expiresAt: now + CONSENT_TTL_MS,
79
+ });
80
+ const url = buildAuthorizeUrl({ clientId: store.clientId, redirectUri, state });
81
+ process.stderr.write(`[quickbooks] op=authorize-url state=${state} realmHint=${realmHint ?? ""}\n`);
82
+ return ok(`Hand this URL to a person to authorize the company (valid 15 minutes):\n\n${url}`);
83
+ }
84
+ catch (err) {
85
+ return fail(`quickbooks-authorize-url failed: ${err.message}`);
86
+ }
87
+ });
88
+ server.tool("quickbooks-connections", "List the QuickBooks companies (realmIds) this account is connected to, with the age of each stored refresh token.", {}, async () => {
89
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
90
+ return refuseNoAccount("quickbooks-connections");
91
+ const store = readStore(PLATFORM_ROOT, ACCOUNT_ID);
92
+ if (!store)
93
+ return ok("No credentials set.");
94
+ const now = Date.now();
95
+ const connections = Object.entries(store.connections).map(([realmId, c]) => ({
96
+ realmId,
97
+ lastRefresh: c.lastRefresh,
98
+ ageDays: c.lastRefresh > 0 ? Math.floor((now - c.lastRefresh) / 86_400_000) : null,
99
+ refreshTokenExpiry: c.refreshTokenExpiry,
100
+ }));
101
+ return ok({ environment: store.environment, connections });
102
+ });
103
+ server.tool("quickbooks-token-audit", "Probe every connected company's refresh token and report which are still alive. Run periodically: a refresh token dies silently after ~100 days of inactivity or operator revocation. alive=false means that company needs operator re-consent before any customer-facing call.", {
104
+ realmId: z.string().optional().describe("Audit just this company; omit to audit all"),
105
+ }, async ({ realmId }) => {
106
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
107
+ return refuseNoAccount("quickbooks-token-audit");
108
+ const store = readStore(PLATFORM_ROOT, ACCOUNT_ID);
109
+ if (!store)
110
+ return ok("No credentials set.");
111
+ const realms = realmId ? [realmId] : Object.keys(store.connections);
112
+ const results = [];
113
+ for (const r of realms) {
114
+ results.push({ realmId: r, ...(await auditConnection(PLATFORM_ROOT, ACCOUNT_ID, r)) });
115
+ }
116
+ return ok({ audited: results });
117
+ });
118
+ server.tool("quickbooks-disconnect", "Remove the stored refresh token for one company. The operator can re-authorize later with quickbooks-authorize-url.", { realmId: z.string().describe("The company realmId to disconnect") }, async ({ realmId }) => {
119
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
120
+ return refuseNoAccount("quickbooks-disconnect");
121
+ removeConnection(PLATFORM_ROOT, ACCOUNT_ID, realmId);
122
+ return ok(`Disconnected realmId=${realmId}.`);
123
+ });
124
+ // ---------------------------------------------------------------------------
125
+ // Read
126
+ // ---------------------------------------------------------------------------
127
+ server.tool("quickbooks-query", "Run a QuickBooks query (the QBO SQL-like language) against a company. Covers every readable entity: e.g. SELECT * FROM Invoice WHERE TxnDate > '2026-01-01', SELECT * FROM Customer, SELECT * FROM Bill.", {
128
+ realmId: z.string().describe("The company realmId"),
129
+ query: z.string().describe("A QBO query, e.g. \"SELECT * FROM Invoice MAXRESULTS 20\""),
130
+ }, async ({ realmId, query }) => {
131
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
132
+ return refuseNoAccount("quickbooks-query");
133
+ try {
134
+ return ok(await qboQuery({ platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID, realmId, query }));
135
+ }
136
+ catch (err) {
137
+ return fail(`quickbooks-query failed: ${err.message}`);
138
+ }
139
+ });
140
+ server.tool("quickbooks-report", "Fetch a named QuickBooks report for a company, e.g. ProfitAndLoss, BalanceSheet, CustomerBalance, AgedReceivables. Params are report-specific (start_date, end_date, etc).", {
141
+ realmId: z.string().describe("The company realmId"),
142
+ report: z.string().describe("Report name, e.g. ProfitAndLoss"),
143
+ params: z.record(z.string()).optional().describe("Report parameters, e.g. { start_date: '2026-01-01', end_date: '2026-03-31' }"),
144
+ }, async ({ realmId, report, params }) => {
145
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
146
+ return refuseNoAccount("quickbooks-report");
147
+ try {
148
+ return ok(await qboReport({ platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID, realmId, report, params }));
149
+ }
150
+ catch (err) {
151
+ return fail(`quickbooks-report failed: ${err.message}`);
152
+ }
153
+ });
154
+ // ---------------------------------------------------------------------------
155
+ // Write — entity-specific convenience tools + a generic fallback.
156
+ // All updates require Id + SyncToken in the body (QBO's optimistic-lock token).
157
+ // ---------------------------------------------------------------------------
158
+ function createTool(toolName, entity, description) {
159
+ server.tool(toolName, description, { realmId: z.string().describe("The company realmId"), body: resourceBody.describe(`${entity} resource body`) }, async ({ realmId, body }) => {
160
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
161
+ return refuseNoAccount(toolName);
162
+ try {
163
+ return ok(await qboCreate({ platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID, realmId, entity, body }));
164
+ }
165
+ catch (err) {
166
+ return fail(`${toolName} failed: ${err.message}`);
167
+ }
168
+ });
169
+ }
170
+ function updateTool(toolName, entity, description) {
171
+ server.tool(toolName, description, { realmId: z.string().describe("The company realmId"), body: resourceBody.describe(`${entity} resource body — MUST include Id and SyncToken`) }, async ({ realmId, body }) => {
172
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
173
+ return refuseNoAccount(toolName);
174
+ try {
175
+ return ok(await qboUpdate({ platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID, realmId, entity, body }));
176
+ }
177
+ catch (err) {
178
+ return fail(`${toolName} failed: ${err.message}`);
179
+ }
180
+ });
181
+ }
182
+ createTool("quickbooks-invoice-create", "Invoice", "Create an invoice in a QuickBooks company.");
183
+ updateTool("quickbooks-invoice-update", "Invoice", "Update an invoice (sparse or full) in a QuickBooks company.");
184
+ createTool("quickbooks-customer-create", "Customer", "Create a customer in a QuickBooks company.");
185
+ updateTool("quickbooks-customer-update", "Customer", "Update a customer in a QuickBooks company.");
186
+ createTool("quickbooks-bill-create", "Bill", "Create a bill (vendor payable) in a QuickBooks company.");
187
+ createTool("quickbooks-payment-create", "Payment", "Record a customer payment in a QuickBooks company.");
188
+ server.tool("quickbooks-entity-create", "Create any QuickBooks entity not covered by a dedicated tool (Vendor, Item, Estimate, CreditMemo, etc). entity is the QBO resource name.", {
189
+ realmId: z.string().describe("The company realmId"),
190
+ entity: z.string().describe("QBO entity name, e.g. Vendor, Item, Estimate"),
191
+ body: resourceBody.describe("The resource body"),
192
+ }, async ({ realmId, entity, body }) => {
193
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
194
+ return refuseNoAccount("quickbooks-entity-create");
195
+ try {
196
+ return ok(await qboCreate({ platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID, realmId, entity, body }));
197
+ }
198
+ catch (err) {
199
+ return fail(`quickbooks-entity-create failed: ${err.message}`);
200
+ }
201
+ });
202
+ server.tool("quickbooks-entity-update", "Update any QuickBooks entity not covered by a dedicated tool. body MUST include Id and SyncToken.", {
203
+ realmId: z.string().describe("The company realmId"),
204
+ entity: z.string().describe("QBO entity name, e.g. Vendor, Item, Estimate"),
205
+ body: resourceBody.describe("The resource body — MUST include Id and SyncToken"),
206
+ }, async ({ realmId, entity, body }) => {
207
+ if (!ACCOUNT_ID || !PLATFORM_ROOT)
208
+ return refuseNoAccount("quickbooks-entity-update");
209
+ try {
210
+ return ok(await qboUpdate({ platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID, realmId, entity, body }));
211
+ }
212
+ catch (err) {
213
+ return fail(`quickbooks-entity-update failed: ${err.message}`);
214
+ }
215
+ });
216
+ const transport = new StdioServerTransport();
217
+ await server.connect(transport);
218
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /** OAuth consent screen. The operator's browser lands here; Intuit redirects
2
+ * back to redirect_uri with code+state+realmId. */
3
+ export declare const INTUIT_AUTHORIZE_URL = "https://appcenter.intuit.com/connect/oauth2";
4
+ /** Token endpoint — same host for sandbox and production (the QBO API base
5
+ * differs by environment, the token endpoint does not). */
6
+ export declare const INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
7
+ /** Accounting scope. openid/profile/email are intentionally omitted — the
8
+ * connector needs company data, not an Intuit identity. */
9
+ export declare const QBO_SCOPE = "com.intuit.quickbooks.accounting";
10
+ export interface TokenResponse {
11
+ accessToken: string;
12
+ refreshToken: string;
13
+ /** seconds until the access token expires (Intuit returns ~3600). */
14
+ expiresIn: number;
15
+ /** seconds until the refresh token expires (Intuit returns ~8640000 ≈ 100d). */
16
+ refreshTokenExpiresIn: number;
17
+ }
18
+ export declare function buildAuthorizeUrl(opts: {
19
+ clientId: string;
20
+ redirectUri: string;
21
+ state: string;
22
+ scope?: string;
23
+ }): string;
24
+ export declare function exchangeCode(opts: {
25
+ clientId: string;
26
+ clientSecret: string;
27
+ code: string;
28
+ redirectUri: string;
29
+ tokenUrl?: string;
30
+ }): Promise<TokenResponse>;
31
+ export declare function refreshAccess(opts: {
32
+ clientId: string;
33
+ clientSecret: string;
34
+ refreshToken: string;
35
+ tokenUrl?: string;
36
+ }): Promise<TokenResponse>;
37
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"AAOA;oDACoD;AACpD,eAAO,MAAM,oBAAoB,gDAAgD,CAAC;AAElF;4DAC4D;AAC5D,eAAO,MAAM,gBAAgB,8DACgC,CAAC;AAE9D;4DAC4D;AAC5D,eAAO,MAAM,SAAS,qCAAqC,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAST;AAiDD,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,aAAa,CAAC,CAOzB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,aAAa,CAAC,CAMzB"}
@@ -0,0 +1,75 @@
1
+ // Intuit OAuth 2.0 — authorization-code grant + refresh. Pure HTTP, no state.
2
+ //
3
+ // Two irreducibly human steps live OUTSIDE this module (relayed, never
4
+ // automated): the operator registers their own Intuit app, and a human clicks
5
+ // the authorize URL to consent per company. Everything here — the URL string,
6
+ // the code->token exchange, the refresh — is deterministic.
7
+ /** OAuth consent screen. The operator's browser lands here; Intuit redirects
8
+ * back to redirect_uri with code+state+realmId. */
9
+ export const INTUIT_AUTHORIZE_URL = "https://appcenter.intuit.com/connect/oauth2";
10
+ /** Token endpoint — same host for sandbox and production (the QBO API base
11
+ * differs by environment, the token endpoint does not). */
12
+ export const INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
13
+ /** Accounting scope. openid/profile/email are intentionally omitted — the
14
+ * connector needs company data, not an Intuit identity. */
15
+ export const QBO_SCOPE = "com.intuit.quickbooks.accounting";
16
+ export function buildAuthorizeUrl(opts) {
17
+ const params = new URLSearchParams({
18
+ client_id: opts.clientId,
19
+ redirect_uri: opts.redirectUri,
20
+ response_type: "code",
21
+ scope: opts.scope ?? QBO_SCOPE,
22
+ state: opts.state,
23
+ });
24
+ return `${INTUIT_AUTHORIZE_URL}?${params.toString()}`;
25
+ }
26
+ function basicAuth(clientId, clientSecret) {
27
+ return "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
28
+ }
29
+ async function postToken(tokenUrl, clientId, clientSecret, form) {
30
+ const res = await fetch(tokenUrl, {
31
+ method: "POST",
32
+ headers: {
33
+ Authorization: basicAuth(clientId, clientSecret),
34
+ "Content-Type": "application/x-www-form-urlencoded",
35
+ Accept: "application/json",
36
+ },
37
+ body: form.toString(),
38
+ });
39
+ const text = await res.text();
40
+ if (!res.ok) {
41
+ throw new Error(`token endpoint ${res.status}: ${text}`);
42
+ }
43
+ let body;
44
+ try {
45
+ body = JSON.parse(text);
46
+ }
47
+ catch {
48
+ throw new Error(`token endpoint returned non-JSON: ${text}`);
49
+ }
50
+ if (!body.access_token || !body.refresh_token) {
51
+ throw new Error(`token endpoint response missing tokens: ${text}`);
52
+ }
53
+ return {
54
+ accessToken: body.access_token,
55
+ refreshToken: body.refresh_token,
56
+ expiresIn: body.expires_in ?? 3600,
57
+ refreshTokenExpiresIn: body.x_refresh_token_expires_in ?? 8_640_000,
58
+ };
59
+ }
60
+ export function exchangeCode(opts) {
61
+ const form = new URLSearchParams({
62
+ grant_type: "authorization_code",
63
+ code: opts.code,
64
+ redirect_uri: opts.redirectUri,
65
+ });
66
+ return postToken(opts.tokenUrl ?? INTUIT_TOKEN_URL, opts.clientId, opts.clientSecret, form);
67
+ }
68
+ export function refreshAccess(opts) {
69
+ const form = new URLSearchParams({
70
+ grant_type: "refresh_token",
71
+ refresh_token: opts.refreshToken,
72
+ });
73
+ return postToken(opts.tokenUrl ?? INTUIT_TOKEN_URL, opts.clientId, opts.clientSecret, form);
74
+ }
75
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export interface QboClientConfig {
2
+ /** Override Intuit's token endpoint (tests). */
3
+ tokenUrl?: string;
4
+ /** Override the QBO API base (tests); defaults from store.environment. */
5
+ apiBase?: string;
6
+ }
7
+ export declare function qboQuery(args: {
8
+ platformRoot: string;
9
+ accountId: string;
10
+ realmId: string;
11
+ query: string;
12
+ }, cfg?: QboClientConfig): Promise<unknown>;
13
+ export declare function qboCreate(args: {
14
+ platformRoot: string;
15
+ accountId: string;
16
+ realmId: string;
17
+ entity: string;
18
+ body: unknown;
19
+ }, cfg?: QboClientConfig): Promise<unknown>;
20
+ export declare function qboUpdate(args: {
21
+ platformRoot: string;
22
+ accountId: string;
23
+ realmId: string;
24
+ entity: string;
25
+ body: unknown;
26
+ }, cfg?: QboClientConfig): Promise<unknown>;
27
+ export declare function auditConnection(platformRoot: string, accountId: string, realmId: string, cfg?: QboClientConfig): Promise<{
28
+ alive: boolean;
29
+ ageDays: number;
30
+ outcome: string;
31
+ }>;
32
+ export declare function qboReport(args: {
33
+ platformRoot: string;
34
+ accountId: string;
35
+ realmId: string;
36
+ report: string;
37
+ params?: Record<string, string>;
38
+ }, cfg?: QboClientConfig): Promise<unknown>;
39
+ //# sourceMappingURL=qbo-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qbo-client.d.ts","sourceRoot":"","sources":["../../src/lib/qbo-client.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgID,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EACjF,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf,EACD,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB;AAID,wBAAgB,SAAS,CACvB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf,EACD,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAElB;AAOD,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAiC/D;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE;IACJ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,EACD,GAAG,GAAE,eAAoB,GACxB,OAAO,CAAC,OAAO,CAAC,CAalB"}
@@ -0,0 +1,178 @@
1
+ // QuickBooks Online REST client.
2
+ //
3
+ // Owns the deterministic half of the OAuth lifecycle: it keeps a short-lived
4
+ // access token fresh by refreshing from the stored refresh token, and persists
5
+ // the rotated refresh token Intuit returns on every refresh. No human is in
6
+ // this path. Emits the op=refresh and op=call observability lines.
7
+ import { readStore, upsertConnection } from "./secrets.js";
8
+ import { refreshAccess } from "./oauth.js";
9
+ const SANDBOX_API_BASE = "https://sandbox-quickbooks.api.intuit.com";
10
+ const PRODUCTION_API_BASE = "https://quickbooks.api.intuit.com";
11
+ /** Refresh when the cached access token is within this window of expiry. */
12
+ const ACCESS_REFRESH_THRESHOLD_MS = 60_000;
13
+ // Per (account, realm) access-token cache. The MCP server is one long-lived
14
+ // process per account, so caching here avoids a refresh on every call.
15
+ const accessCache = new Map();
16
+ function cacheKey(accountId, realmId) {
17
+ return `${accountId}:${realmId}`;
18
+ }
19
+ function classifyRefreshError(message) {
20
+ if (message.includes("invalid_grant"))
21
+ return "invalid_grant";
22
+ if (message.includes("token endpoint 401"))
23
+ return "401";
24
+ return "error";
25
+ }
26
+ async function ensureAccessToken(platformRoot, accountId, realmId, cfg) {
27
+ const store = readStore(platformRoot, accountId);
28
+ if (!store)
29
+ throw new Error("no QuickBooks credentials configured for this account");
30
+ const conn = store.connections[realmId];
31
+ if (!conn)
32
+ throw new Error(`no QuickBooks connection for realmId=${realmId}`);
33
+ const apiBase = cfg.apiBase ?? (store.environment === "production" ? PRODUCTION_API_BASE : SANDBOX_API_BASE);
34
+ const key = cacheKey(accountId, realmId);
35
+ const cached = accessCache.get(key);
36
+ if (cached && cached.expiresAt - ACCESS_REFRESH_THRESHOLD_MS > Date.now()) {
37
+ return { accessToken: cached.accessToken, apiBase };
38
+ }
39
+ const started = Date.now();
40
+ try {
41
+ const tok = await refreshAccess({
42
+ clientId: store.clientId,
43
+ clientSecret: store.clientSecret,
44
+ refreshToken: conn.refreshToken,
45
+ tokenUrl: cfg.tokenUrl,
46
+ });
47
+ const now = Date.now();
48
+ // Intuit rotates the refresh token on every refresh — persist the new one
49
+ // or the next refresh fails with invalid_grant.
50
+ upsertConnection(platformRoot, accountId, realmId, {
51
+ refreshToken: tok.refreshToken,
52
+ refreshTokenExpiry: now + tok.refreshTokenExpiresIn * 1000,
53
+ lastRefresh: now,
54
+ });
55
+ accessCache.set(key, { accessToken: tok.accessToken, expiresAt: now + tok.expiresIn * 1000 });
56
+ process.stderr.write(`[quickbooks] op=refresh realmId=${realmId} outcome=ok ms=${now - started}\n`);
57
+ return { accessToken: tok.accessToken, apiBase };
58
+ }
59
+ catch (err) {
60
+ const message = err instanceof Error ? err.message : String(err);
61
+ const outcome = classifyRefreshError(message);
62
+ process.stderr.write(`[quickbooks] op=refresh realmId=${realmId} outcome=${outcome} ms=${Date.now() - started}\n`);
63
+ throw err;
64
+ }
65
+ }
66
+ async function qboRequest(args, cfg = {}) {
67
+ const { accessToken, apiBase } = await ensureAccessToken(args.platformRoot, args.accountId, args.realmId, cfg);
68
+ // Encode with encodeURIComponent (→ %20 for spaces) rather than
69
+ // URLSearchParams (→ + for spaces): the QBO query endpoint expects %20 and a
70
+ // + would be read as a literal plus inside a SQL-ish WHERE clause.
71
+ const query = { minorversion: "73", ...(args.search ?? {}) };
72
+ const search = Object.entries(query)
73
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
74
+ .join("&");
75
+ const url = `${apiBase}/v3/company/${args.realmId}/${args.urlPath}?${search}`;
76
+ const headers = {
77
+ Authorization: `Bearer ${accessToken}`,
78
+ Accept: "application/json",
79
+ };
80
+ if (args.body !== undefined)
81
+ headers["Content-Type"] = "application/json";
82
+ const started = Date.now();
83
+ const res = await fetch(url, {
84
+ method: args.method,
85
+ headers,
86
+ body: args.body !== undefined ? JSON.stringify(args.body) : undefined,
87
+ });
88
+ const text = await res.text();
89
+ process.stderr.write(`[quickbooks] op=call realmId=${args.realmId} entity=${args.entity} method=${args.method} status=${res.status} ms=${Date.now() - started}\n`);
90
+ if (!res.ok) {
91
+ throw new Error(`QBO ${args.method} ${args.urlPath} ${res.status}: ${text}`);
92
+ }
93
+ try {
94
+ return JSON.parse(text);
95
+ }
96
+ catch {
97
+ throw new Error(`QBO ${args.urlPath} returned non-JSON: ${text}`);
98
+ }
99
+ }
100
+ export function qboQuery(args, cfg = {}) {
101
+ return qboRequest({
102
+ platformRoot: args.platformRoot,
103
+ accountId: args.accountId,
104
+ realmId: args.realmId,
105
+ method: "GET",
106
+ urlPath: "query",
107
+ search: { query: args.query },
108
+ entity: "query",
109
+ }, cfg);
110
+ }
111
+ export function qboCreate(args, cfg = {}) {
112
+ return qboRequest({
113
+ platformRoot: args.platformRoot,
114
+ accountId: args.accountId,
115
+ realmId: args.realmId,
116
+ method: "POST",
117
+ urlPath: args.entity.toLowerCase(),
118
+ entity: args.entity,
119
+ body: args.body,
120
+ }, cfg);
121
+ }
122
+ // Update is the same endpoint as create — a full or sparse POST carrying Id +
123
+ // SyncToken. The caller assembles the body; the connector does not guess it.
124
+ export function qboUpdate(args, cfg = {}) {
125
+ return qboCreate(args, cfg);
126
+ }
127
+ // Standing no-event-failure check. A refresh token dies silently after ~100
128
+ // days of inactivity or operator revocation — nothing is emitted until the next
129
+ // call returns invalid_grant. auditConnection forces a refresh probe so a dead
130
+ // connection is surfaced BEFORE a customer-facing call hits it. A successful
131
+ // probe also keeps the token alive (refresh resets the 100-day clock).
132
+ export async function auditConnection(platformRoot, accountId, realmId, cfg = {}) {
133
+ const store = readStore(platformRoot, accountId);
134
+ const conn = store?.connections[realmId];
135
+ if (!store || !conn) {
136
+ process.stderr.write(`[quickbooks] op=token-audit realmId=${realmId} alive=false ageDays=-1\n`);
137
+ return { alive: false, ageDays: -1, outcome: "no-connection" };
138
+ }
139
+ const ageDays = conn.lastRefresh > 0 ? Math.floor((Date.now() - conn.lastRefresh) / 86_400_000) : -1;
140
+ try {
141
+ const tok = await refreshAccess({
142
+ clientId: store.clientId,
143
+ clientSecret: store.clientSecret,
144
+ refreshToken: conn.refreshToken,
145
+ tokenUrl: cfg.tokenUrl,
146
+ });
147
+ const now = Date.now();
148
+ upsertConnection(platformRoot, accountId, realmId, {
149
+ refreshToken: tok.refreshToken,
150
+ refreshTokenExpiry: now + tok.refreshTokenExpiresIn * 1000,
151
+ lastRefresh: now,
152
+ });
153
+ accessCache.set(cacheKey(accountId, realmId), {
154
+ accessToken: tok.accessToken,
155
+ expiresAt: now + tok.expiresIn * 1000,
156
+ });
157
+ process.stderr.write(`[quickbooks] op=token-audit realmId=${realmId} alive=true ageDays=${ageDays}\n`);
158
+ return { alive: true, ageDays, outcome: "ok" };
159
+ }
160
+ catch (err) {
161
+ const message = err instanceof Error ? err.message : String(err);
162
+ const outcome = classifyRefreshError(message);
163
+ process.stderr.write(`[quickbooks] op=token-audit realmId=${realmId} alive=false ageDays=${ageDays}\n`);
164
+ return { alive: false, ageDays, outcome };
165
+ }
166
+ }
167
+ export function qboReport(args, cfg = {}) {
168
+ return qboRequest({
169
+ platformRoot: args.platformRoot,
170
+ accountId: args.accountId,
171
+ realmId: args.realmId,
172
+ method: "GET",
173
+ urlPath: `reports/${args.report}`,
174
+ search: args.params ?? {},
175
+ entity: `report:${args.report}`,
176
+ }, cfg);
177
+ }
178
+ //# sourceMappingURL=qbo-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qbo-client.js","sourceRoot":"","sources":["../../src/lib/qbo-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export interface QbConnection {
2
+ refreshToken: string;
3
+ /** epoch ms when the refresh token itself expires (~100 days of inactivity). */
4
+ refreshTokenExpiry: number;
5
+ /** epoch ms of the last successful token exchange or refresh. */
6
+ lastRefresh: number;
7
+ }
8
+ export interface QbStore {
9
+ clientId: string;
10
+ clientSecret: string;
11
+ environment: "sandbox" | "production";
12
+ /** keyed by Intuit realmId (one company per realm). */
13
+ connections: Record<string, QbConnection>;
14
+ }
15
+ export interface QbPending {
16
+ state: string;
17
+ /** The exact redirect_uri used in the authorize request — the token exchange
18
+ * must echo it verbatim or Intuit rejects the code. */
19
+ redirectUri: string;
20
+ realmHint?: string;
21
+ createdAt: number;
22
+ expiresAt: number;
23
+ }
24
+ export declare function accountSecretsDir(platformRoot: string, accountId: string): string;
25
+ export declare function readStore(platformRoot: string, accountId: string): QbStore | null;
26
+ export declare function setCredentials(platformRoot: string, accountId: string, creds: {
27
+ clientId: string;
28
+ clientSecret: string;
29
+ environment: "sandbox" | "production";
30
+ }): void;
31
+ export declare function upsertConnection(platformRoot: string, accountId: string, realmId: string, conn: QbConnection): void;
32
+ export declare function removeConnection(platformRoot: string, accountId: string, realmId: string): void;
33
+ export declare function writePending(platformRoot: string, accountId: string, pending: QbPending): void;
34
+ export declare function readPending(platformRoot: string, accountId: string, state: string): QbPending | null;
35
+ export declare function deletePending(platformRoot: string, accountId: string, state: string): void;
36
+ //# sourceMappingURL=secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,SAAS,GAAG,YAAY,CAAC;IACtC,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd;4DACwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjF;AAiBD,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQjF;AAUD,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,SAAS,GAAG,YAAY,CAAA;CAAE,GACvF,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,GACjB,IAAI,CAON;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,IAAI,CAKN;AAED,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,GACjB,IAAI,CAIN;AAED,wBAAgB,WAAW,CACzB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,SAAS,GAAG,IAAI,CAalB;AAED,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,IAAI,CAQN"}