@wootsup/mcp 0.1.0 → 0.3.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.
Files changed (131) hide show
  1. package/CHANGELOG.md +148 -83
  2. package/README.md +31 -27
  3. package/SECURITY.md +15 -6
  4. package/dist/auth/keychain.d.ts +27 -1
  5. package/dist/auth/keychain.js +48 -2
  6. package/dist/auth/keychain.js.map +1 -1
  7. package/dist/cli-hint.d.ts +22 -0
  8. package/dist/cli-hint.js +55 -0
  9. package/dist/cli-hint.js.map +1 -0
  10. package/dist/index.js +97 -22
  11. package/dist/index.js.map +1 -1
  12. package/dist/install-skill.js +1 -1
  13. package/dist/modules/apimapper/cache.js +25 -17
  14. package/dist/modules/apimapper/cache.js.map +1 -1
  15. package/dist/modules/apimapper/client.d.ts +62 -1
  16. package/dist/modules/apimapper/client.js +555 -291
  17. package/dist/modules/apimapper/client.js.map +1 -1
  18. package/dist/modules/apimapper/connections.js +230 -75
  19. package/dist/modules/apimapper/connections.js.map +1 -1
  20. package/dist/modules/apimapper/credential-sanitizer.d.ts +5 -0
  21. package/dist/modules/apimapper/credential-sanitizer.js +60 -1
  22. package/dist/modules/apimapper/credential-sanitizer.js.map +1 -1
  23. package/dist/modules/apimapper/credentials.js +19 -47
  24. package/dist/modules/apimapper/credentials.js.map +1 -1
  25. package/dist/modules/apimapper/diagnose.js +21 -2
  26. package/dist/modules/apimapper/diagnose.js.map +1 -1
  27. package/dist/modules/apimapper/flows.js +60 -77
  28. package/dist/modules/apimapper/flows.js.map +1 -1
  29. package/dist/modules/apimapper/gateway/advanced-tool.js +56 -5
  30. package/dist/modules/apimapper/gateway/advanced-tool.js.map +1 -1
  31. package/dist/modules/apimapper/gateway/essentials.d.ts +1 -1
  32. package/dist/modules/apimapper/gateway/essentials.js +8 -1
  33. package/dist/modules/apimapper/gateway/essentials.js.map +1 -1
  34. package/dist/modules/apimapper/get-skill.d.ts +1 -1
  35. package/dist/modules/apimapper/get-skill.js +44 -6
  36. package/dist/modules/apimapper/get-skill.js.map +1 -1
  37. package/dist/modules/apimapper/graph.js +40 -36
  38. package/dist/modules/apimapper/graph.js.map +1 -1
  39. package/dist/modules/apimapper/index.js +2 -0
  40. package/dist/modules/apimapper/index.js.map +1 -1
  41. package/dist/modules/apimapper/library.js +425 -83
  42. package/dist/modules/apimapper/library.js.map +1 -1
  43. package/dist/modules/apimapper/license.js +12 -36
  44. package/dist/modules/apimapper/license.js.map +1 -1
  45. package/dist/modules/apimapper/local-sources.js +20 -34
  46. package/dist/modules/apimapper/local-sources.js.map +1 -1
  47. package/dist/modules/apimapper/misc.js +13 -27
  48. package/dist/modules/apimapper/misc.js.map +1 -1
  49. package/dist/modules/apimapper/onboarding.d.ts +30 -1
  50. package/dist/modules/apimapper/onboarding.js +114 -19
  51. package/dist/modules/apimapper/onboarding.js.map +1 -1
  52. package/dist/modules/apimapper/schema.js +9 -18
  53. package/dist/modules/apimapper/schema.js.map +1 -1
  54. package/dist/modules/apimapper/settings.js +49 -52
  55. package/dist/modules/apimapper/settings.js.map +1 -1
  56. package/dist/modules/apimapper/sites-tools.d.ts +29 -0
  57. package/dist/modules/apimapper/sites-tools.js +165 -0
  58. package/dist/modules/apimapper/sites-tools.js.map +1 -0
  59. package/dist/modules/apimapper/tool-result.d.ts +46 -0
  60. package/dist/modules/apimapper/tool-result.js +63 -0
  61. package/dist/modules/apimapper/tool-result.js.map +1 -0
  62. package/dist/modules/apimapper/toolslist-size.d.ts +11 -10
  63. package/dist/modules/apimapper/toolslist-size.js +16 -14
  64. package/dist/modules/apimapper/toolslist-size.js.map +1 -1
  65. package/dist/modules/apimapper/types.d.ts +21 -0
  66. package/dist/modules/apimapper/types.js.map +1 -1
  67. package/dist/modules/apimapper/whitelist-drift.d.ts +85 -0
  68. package/dist/modules/apimapper/whitelist-drift.js +360 -0
  69. package/dist/modules/apimapper/whitelist-drift.js.map +1 -0
  70. package/dist/modules/apimapper/workflows.js +82 -27
  71. package/dist/modules/apimapper/workflows.js.map +1 -1
  72. package/dist/modules/apimapper/yootheme-binding.d.ts +35 -0
  73. package/dist/modules/apimapper/yootheme-binding.js +186 -0
  74. package/dist/modules/apimapper/yootheme-binding.js.map +1 -0
  75. package/dist/platform/index.d.ts +56 -0
  76. package/dist/platform/index.js +151 -2
  77. package/dist/platform/index.js.map +1 -1
  78. package/dist/setup/detect-clients.d.ts +40 -1
  79. package/dist/setup/detect-clients.js +148 -1
  80. package/dist/setup/detect-clients.js.map +1 -1
  81. package/dist/setup/probe-handshake.js +40 -7
  82. package/dist/setup/probe-handshake.js.map +1 -1
  83. package/dist/setup/remove-config.d.ts +8 -0
  84. package/dist/setup/remove-config.js +145 -0
  85. package/dist/setup/remove-config.js.map +1 -0
  86. package/dist/setup/uninstall.d.ts +34 -0
  87. package/dist/setup/uninstall.js +147 -0
  88. package/dist/setup/uninstall.js.map +1 -0
  89. package/dist/setup-cli.d.ts +7 -0
  90. package/dist/setup-cli.js +29 -1
  91. package/dist/setup-cli.js.map +1 -1
  92. package/dist/sites/loader.d.ts +41 -0
  93. package/dist/sites/loader.js +119 -0
  94. package/dist/sites/loader.js.map +1 -0
  95. package/dist/sites/schema.d.ts +69 -0
  96. package/dist/sites/schema.js +71 -0
  97. package/dist/sites/schema.js.map +1 -0
  98. package/dist/sites/secret-resolver.d.ts +47 -0
  99. package/dist/sites/secret-resolver.js +150 -0
  100. package/dist/sites/secret-resolver.js.map +1 -0
  101. package/dist/skill-instructions.d.ts +1 -1
  102. package/dist/skill-instructions.js +5 -0
  103. package/dist/skill-instructions.js.map +1 -1
  104. package/dist/transports/stdio.js +4 -4
  105. package/dist/transports/stdio.js.map +1 -1
  106. package/dist/uninstall-skill.d.ts +27 -0
  107. package/dist/uninstall-skill.js +89 -0
  108. package/dist/uninstall-skill.js.map +1 -0
  109. package/docs/architecture.md +21 -21
  110. package/docs/customgraph-internal-migration.md +4 -4
  111. package/docs/security.md +2 -21
  112. package/docs/tools.md +40 -12
  113. package/manifest.json +77 -79
  114. package/package.json +68 -65
  115. package/skills/apimapper/SKILL.md +53 -7
  116. package/skills/apimapper/reference/conditional-style-multi-items.md +114 -0
  117. package/skills/apimapper/reference/jmespath-pitfalls.md +108 -0
  118. package/skills/apimapper/reference/joomla.md +1 -1
  119. package/skills/apimapper/reference/library-template-discovery.md +65 -0
  120. package/skills/apimapper/reference/merge-two-sources-on-key.md +99 -0
  121. package/skills/apimapper/reference/troubleshooting.md +20 -0
  122. package/skills/apimapper/reference/yootheme.md +1 -1
  123. package/dist/auth/oauth-provider.d.ts +0 -68
  124. package/dist/auth/oauth-provider.js +0 -232
  125. package/dist/auth/oauth-provider.js.map +0 -1
  126. package/dist/server-http.d.ts +0 -22
  127. package/dist/server-http.js +0 -159
  128. package/dist/server-http.js.map +0 -1
  129. package/dist/transports/http.d.ts +0 -29
  130. package/dist/transports/http.js +0 -267
  131. package/dist/transports/http.js.map +0 -1
@@ -0,0 +1,47 @@
1
+ import { type SiteEntryT } from "./schema.js";
2
+ /**
3
+ * Resolve the `op` binary to an absolute path so it works under a stripped-down
4
+ * GUI PATH. Precedence: explicit `APIMAPPER_OP_BINARY` env override → first
5
+ * existing well-known absolute path → bare `op` (PATH).
6
+ */
7
+ export declare function resolveOpBinary(env?: NodeJS.ProcessEnv): string;
8
+ /** Resolved bearer + its provenance for downstream logs (never the token). */
9
+ export interface ResolvedBearer {
10
+ readonly token: string;
11
+ readonly source: "plain" | "op";
12
+ }
13
+ /** Domain-tagged error so callers can branch on `code`. */
14
+ export declare class SecretResolverError extends Error {
15
+ readonly code: string;
16
+ constructor(code: string, message: string);
17
+ }
18
+ /**
19
+ * Signature of `node:child_process.execFile` reduced to the callback we rely
20
+ * on. Injecting this lets tests provide a deterministic substitute without a
21
+ * `vi.mock('node:child_process')` side-effect.
22
+ */
23
+ export type ExecFileLike = (file: string, args: readonly string[], options: {
24
+ timeout?: number;
25
+ maxBuffer?: number;
26
+ }, callback: (error: (Error & {
27
+ code?: string | number;
28
+ signal?: string;
29
+ }) | null, stdout: string, stderr: string) => void) => void;
30
+ export interface ResolveSiteBearerOptions {
31
+ /** Injected execFile for tests. Defaults to Node's real execFile. */
32
+ execFile?: ExecFileLike;
33
+ /** Resolved `op` binary path. Defaults to {@link resolveOpBinary}. */
34
+ opBinary?: string;
35
+ /** Per-call timeout override. Defaults to {@link OP_TIMEOUT_MS}. */
36
+ timeoutMs?: number;
37
+ }
38
+ /**
39
+ * Resolve the bearer token for a site entry.
40
+ *
41
+ * - `bearer` present → returned verbatim (`source:'plain'`).
42
+ * - `bearer_ref` set → `op read <ref>` via execFile (`source:'op'`).
43
+ * - neither → throws SecretResolverError('BEARER_MISSING').
44
+ *
45
+ * Never logs the token; never mutates the input site.
46
+ */
47
+ export declare function resolveSiteBearer(site: SiteEntryT, opts?: ResolveSiteBearerOptions): Promise<ResolvedBearer>;
@@ -0,0 +1,150 @@
1
+ // src/sites/secret-resolver.ts — Phase 3.
2
+ //
3
+ // Produce a bearer token for a SiteEntryT at call time, supporting either a
4
+ // plain in-file token (`bearer`) or a 1Password Secret Reference (`bearer_ref`)
5
+ // resolved through the `op` CLI. Mirrors the YT Builder defence model
6
+ // (yt-builder-mcp/packages/mcp/src/sites/secret-resolver.ts), kept simple:
7
+ //
8
+ // - `execFile`, NEVER `exec`/`spawn` with `shell:true`. `op read <ref>` runs
9
+ // as a direct argv invocation — no shell, no metacharacter expansion. The
10
+ // schema enforces OP_REF_REGEX on load; we re-enforce it here as
11
+ // defence-in-depth (a hand-edited sites.json could otherwise sneak a
12
+ // payload past validation).
13
+ // - Hard timeout so a biometric-stalled `op` can't wedge the agent loop.
14
+ // - Pluggable `execFile` for testing — no global child_process monkey-patch.
15
+ import { execFile as nodeExecFile } from "node:child_process";
16
+ import { existsSync } from "node:fs";
17
+ import { OP_REF_REGEX } from "./schema.js";
18
+ /** Hard cap for any single `op read` invocation. */
19
+ const OP_TIMEOUT_MS = 5_000;
20
+ /**
21
+ * Common absolute install locations for the 1Password CLI. GUI-spawned MCP
22
+ * hosts (Claude Desktop, etc.) launch the server subprocess with a minimal
23
+ * PATH that usually omits `/opt/homebrew/bin` and `/usr/local/bin`, so a bare
24
+ * `op` resolves to ENOENT even when `op` works in the user's shell. Probe these
25
+ * absolute paths before falling back to PATH.
26
+ */
27
+ const OP_BINARY_CANDIDATES = [
28
+ "/opt/homebrew/bin/op",
29
+ "/usr/local/bin/op",
30
+ "/usr/bin/op",
31
+ ];
32
+ /**
33
+ * Resolve the `op` binary to an absolute path so it works under a stripped-down
34
+ * GUI PATH. Precedence: explicit `APIMAPPER_OP_BINARY` env override → first
35
+ * existing well-known absolute path → bare `op` (PATH).
36
+ */
37
+ export function resolveOpBinary(env = process.env) {
38
+ const override = (env.APIMAPPER_OP_BINARY ?? "").trim();
39
+ if (override.length > 0)
40
+ return override;
41
+ for (const candidate of OP_BINARY_CANDIDATES) {
42
+ try {
43
+ if (existsSync(candidate))
44
+ return candidate;
45
+ }
46
+ catch {
47
+ // ignore stat errors and keep probing
48
+ }
49
+ }
50
+ return "op";
51
+ }
52
+ /** Domain-tagged error so callers can branch on `code`. */
53
+ export class SecretResolverError extends Error {
54
+ code;
55
+ constructor(code, message) {
56
+ super(message);
57
+ this.code = code;
58
+ this.name = "SecretResolverError";
59
+ Object.setPrototypeOf(this, SecretResolverError.prototype);
60
+ }
61
+ }
62
+ /** Default adapter wrapping Node's real `execFile` with utf8 encoding. */
63
+ const defaultExecFile = (file, args, options, callback) => {
64
+ nodeExecFile(file, args, { ...options, encoding: "utf8" }, (err, stdout, stderr) => {
65
+ callback(err, stdout, stderr);
66
+ });
67
+ };
68
+ const OP_EXIT_CODE_HINTS = {
69
+ 6: "Not signed in to 1Password CLI. Run `op signin` and retry.",
70
+ 1: "Generic op CLI error — verify the secret reference exists and the vault is accessible.",
71
+ };
72
+ /**
73
+ * Resolve the bearer token for a site entry.
74
+ *
75
+ * - `bearer` present → returned verbatim (`source:'plain'`).
76
+ * - `bearer_ref` set → `op read <ref>` via execFile (`source:'op'`).
77
+ * - neither → throws SecretResolverError('BEARER_MISSING').
78
+ *
79
+ * Never logs the token; never mutates the input site.
80
+ */
81
+ export async function resolveSiteBearer(site, opts = {}) {
82
+ if (site.bearer !== undefined && site.bearer.length > 0) {
83
+ return { token: site.bearer, source: "plain" };
84
+ }
85
+ const ref = site.bearer_ref;
86
+ if (ref === undefined || ref.length === 0) {
87
+ throw new SecretResolverError("BEARER_MISSING", `Site '${site.site_id}' has neither an inline 'bearer' nor a 'bearer_ref'.`);
88
+ }
89
+ // Defence-in-depth: re-validate the ref shape BEFORE it reaches a subprocess
90
+ // argv. The schema enforces this on load; a hand-edited sites.json could
91
+ // bypass that.
92
+ if (!OP_REF_REGEX.test(ref)) {
93
+ throw new SecretResolverError("OP_REF_INVALID", `Site '${site.site_id}' has an invalid 'bearer_ref' shape — must match op://<vault>/<item>/<field>.`);
94
+ }
95
+ const execFile = opts.execFile ?? defaultExecFile;
96
+ const opBinary = opts.opBinary ?? resolveOpBinary();
97
+ const timeoutMs = opts.timeoutMs ?? OP_TIMEOUT_MS;
98
+ const stdout = await runOpRead(execFile, opBinary, ref, timeoutMs, site.site_id);
99
+ const token = stdout.trim();
100
+ if (token.length === 0) {
101
+ throw new SecretResolverError("OP_EMPTY_OUTPUT", `op returned empty output for '${ref}' — verify the field exists and is non-empty.`);
102
+ }
103
+ return { token, source: "op" };
104
+ }
105
+ /** Invoke `op read <ref>` with a hard timeout that survives a stalled child. */
106
+ function runOpRead(execFile, opBinary, ref, timeoutMs, siteId) {
107
+ return new Promise((resolve, reject) => {
108
+ let settled = false;
109
+ const timer = setTimeout(() => {
110
+ if (settled)
111
+ return;
112
+ settled = true;
113
+ reject(new SecretResolverError("OP_TIMEOUT", `op read '${ref}' exceeded ${timeoutMs}ms — is 1Password CLI hung on a biometric prompt?`));
114
+ }, timeoutMs);
115
+ if (typeof timer.unref === "function")
116
+ timer.unref();
117
+ execFile(opBinary, ["read", ref], { timeout: timeoutMs, maxBuffer: 1024 * 64 }, (err, stdout, stderr) => {
118
+ if (settled)
119
+ return;
120
+ settled = true;
121
+ clearTimeout(timer);
122
+ if (err) {
123
+ reject(mapExecError(err, stderr, ref, siteId));
124
+ return;
125
+ }
126
+ resolve(stdout);
127
+ });
128
+ });
129
+ }
130
+ function mapExecError(err, stderr, ref, siteId) {
131
+ const codeStr = typeof err.code === "string" ? err.code : "";
132
+ const codeNum = typeof err.code === "number" ? err.code : NaN;
133
+ if (codeStr === "ENOENT") {
134
+ return new SecretResolverError("OP_CLI_MISSING", `op CLI not found for site '${siteId}' — install 1Password CLI (or set APIMAPPER_OP_BINARY), ` +
135
+ "or use an inline 'bearer' field instead of 'bearer_ref'.");
136
+ }
137
+ const stderrLower = (stderr || "").toLowerCase();
138
+ if (stderrLower.includes("isn't an item") || stderrLower.includes("item not found")) {
139
+ return new SecretResolverError("OP_ITEM_NOT_FOUND", `1Password item not found at '${ref}'. Verify the ref points to an existing item.`);
140
+ }
141
+ if (Number.isFinite(codeNum)) {
142
+ const hint = OP_EXIT_CODE_HINTS[codeNum];
143
+ if (codeNum === 6) {
144
+ return new SecretResolverError("OP_NOT_SIGNED_IN", hint ?? "op exited 6 — not signed in.");
145
+ }
146
+ return new SecretResolverError("OP_EXEC_FAILED", `op read '${ref}' failed with exit ${codeNum}: ${(stderr || "").trim() || (hint ?? "no stderr")}`);
147
+ }
148
+ return new SecretResolverError("OP_EXEC_FAILED", `op read '${ref}' failed: ${err.message}`);
149
+ }
150
+ //# sourceMappingURL=secret-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-resolver.js","sourceRoot":"","sources":["../../src/sites/secret-resolver.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ export declare const FALLBACK_INSTRUCTIONS = "Use this MCP to manage API Mapper
8
8
  *
9
9
  * Topics MUST stay in sync with SKILL_TOPICS in modules/apimapper/get-skill.ts.
10
10
  */
11
- export declare const SKILL_TOPIC_POINTER = "\n\n## Available skill topics\n\nFor deeper guidance on specific aspects of API Mapper, call:\n apimapper_get_skill({ topic })\n\nAvailable topics:\n- getting-started \u2014 Quickstart, common tools, common pitfalls\n- oauth \u2014 Wiring OAuth-protected sources (Google, Pexels, Instagram, Notion, ...)\n- yootheme \u2014 Publish flows as YOOtheme sources; Save-vs-Publish\n- joomla \u2014 Joomla 3-tier auth, com_ajax envelope, system plugin layout\n- troubleshooting \u2014 HTTP status triage, flow issues, credential states\n";
11
+ export declare const SKILL_TOPIC_POINTER = "\n\n## Available skill topics\n\nFor deeper guidance on specific aspects of API Mapper, call:\n apimapper_get_skill({ topic })\n\nAvailable topics:\n- getting-started \u2014 Quickstart, common tools, common pitfalls\n- oauth \u2014 Wiring OAuth-protected sources (Google, Pexels, Instagram, Notion, ...)\n- yootheme \u2014 Publish flows as YOOtheme sources; Save-vs-Publish\n- joomla \u2014 Joomla 3-tier auth, com_ajax envelope, system plugin layout\n- troubleshooting \u2014 HTTP status triage, flow issues, credential states\n- render \u2014 Render a flow as a diagram (viz-type, flow-diagram output)\n- jmespath-pitfalls \u2014 The 9 traps + date-primitive functions (date_weekday, date_iso_to_time, date_iso_to_date, time_in_window) + depth-cap warning\n- merge-two-sources-on-key \u2014 Joining two sources on a shared key, including date-primitive bridging for weekday-name \u2194 ISO-datetime joins\n- conditional-style-multi-items \u2014 Per-row YOOtheme styles driven by a row field (text_color, panel_style)\n- library-template-discovery \u2014 Inspect a library template's contract before activating\n";
12
12
  /**
13
13
  * Loads MCP server instructions from skills/apimapper/SKILL.md.
14
14
  *
@@ -30,6 +30,11 @@ Available topics:
30
30
  - yootheme — Publish flows as YOOtheme sources; Save-vs-Publish
31
31
  - joomla — Joomla 3-tier auth, com_ajax envelope, system plugin layout
32
32
  - troubleshooting — HTTP status triage, flow issues, credential states
33
+ - render — Render a flow as a diagram (viz-type, flow-diagram output)
34
+ - jmespath-pitfalls — The 9 traps + date-primitive functions (date_weekday, date_iso_to_time, date_iso_to_date, time_in_window) + depth-cap warning
35
+ - merge-two-sources-on-key — Joining two sources on a shared key, including date-primitive bridging for weekday-name ↔ ISO-datetime joins
36
+ - conditional-style-multi-items — Per-row YOOtheme styles driven by a row field (text_color, panel_style)
37
+ - library-template-discovery — Inspect a library template's contract before activating
33
38
  `;
34
39
  /**
35
40
  * Loads MCP server instructions from skills/apimapper/SKILL.md.
@@ -1 +1 @@
1
- {"version":3,"file":"skill-instructions.js","sourceRoot":"","sources":["../src/skill-instructions.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,yCAAyC;AACzC,EAAE;AACF,yEAAyE;AAEzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAChC,8FAA8F,CAAC;AAEjG,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;CAalC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,OAAO,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAEnE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IACD,yEAAyE;IACzE,qEAAqE;IACrE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,mBAAmB,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,iDAAiD;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,kEAAkE;IAClE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,qDAAqD;QACrD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,WAAW;IAClB,8DAA8D;IAC9D,qEAAqE;IACrE,wCAAwC;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"skill-instructions.js","sourceRoot":"","sources":["../src/skill-instructions.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,yCAAyC;AACzC,EAAE;AACF,yEAAyE;AAEzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,CAAC,MAAM,qBAAqB,GAChC,8FAA8F,CAAC;AAEjG,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;CAkBlC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,OAAO,GAAG,OAAO,IAAI,WAAW,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;IAEnE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,qBAAqB,GAAG,mBAAmB,CAAC;IACrD,CAAC;IACD,yEAAyE;IACzE,qEAAqE;IACrE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,mBAAmB,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,iDAAiD;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,kEAAkE;IAClE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,qDAAqD;QACrD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,WAAW;IAClB,8DAA8D;IAC9D,qEAAqE;IACrE,wCAAwC;IACxC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC7B,CAAC"}
@@ -1,9 +1,9 @@
1
- // src/transports/stdio.ts — Phase 9.1.
1
+ // src/transports/stdio.ts
2
2
  //
3
3
  // Thin factory that connects an McpServer to a StdioServerTransport so the
4
- // process speaks JSON-RPC over stdin/stdout. Extracted from src/index.ts as
5
- // part of the multi-transport split Task 9.2 introduces an HTTP variant
6
- // using the same `connect*` shape.
4
+ // process speaks JSON-RPC over stdin/stdout. stdio is the only transport the
5
+ // MCP server ships it runs over stdio for both the npm/npx path and the
6
+ // Claude Desktop DXT bundle.
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  /**
9
9
  * Connect the given McpServer to a fresh `StdioServerTransport`.
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,0EAA0E;AAC1E,mCAAmC;AAGnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAiB;IAClD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/transports/stdio.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,6BAA6B;AAG7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAiB;IAClD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,27 @@
1
+ export interface UninstallSkillOptions {
2
+ /** Skills dir holding the `apimapper/` folder. Default: ~/.claude/skills/ */
3
+ targetSkillsDir?: string;
4
+ /** AGENTS.md file the marker was appended to. Default: ~/AGENTS.md */
5
+ targetAgentsFile?: string;
6
+ }
7
+ export interface UninstallSkillResult {
8
+ skillRemoved: boolean;
9
+ markerRemoved: boolean;
10
+ }
11
+ /**
12
+ * Strip the install-skill marker block from AGENTS.md content.
13
+ *
14
+ * install-skill appends a block of the shape:
15
+ *
16
+ * \n\n## API Mapper MCP\n\n<MARKER_LINE>\n<one descriptive line>\n
17
+ *
18
+ * We remove the `## API Mapper MCP` heading (and any leading blank lines
19
+ * before it) through the descriptive line that follows the marker. The match
20
+ * is anchored on MARKER_LINE so an edited heading still gets cleaned as long
21
+ * as the marker is intact. Returns the cleaned text, or `null` if no marker
22
+ * was present (caller treats that as "nothing removed").
23
+ */
24
+ export declare function stripMarkerBlock(content: string): string | null;
25
+ export declare function uninstallSkill(options?: UninstallSkillOptions): Promise<UninstallSkillResult>;
26
+ /** True iff the skill dir is present (drives the default skill-removal prompt). */
27
+ export declare function isSkillInstalled(targetSkillsDir?: string): boolean;
@@ -0,0 +1,89 @@
1
+ // src/uninstall-skill.ts — reverse of install-skill.ts.
2
+ //
3
+ // uninstallSkill() removes the copied skills/apimapper/ directory and strips
4
+ // the marker block that install-skill appended to AGENTS.md. Idempotent: a
5
+ // missing skill dir or absent marker is a clean no-op. Preserves every other
6
+ // skill + all other AGENTS.md content.
7
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { homedir } from "node:os";
10
+ import { MARKER_LINE } from "./install-skill.js";
11
+ /**
12
+ * Strip the install-skill marker block from AGENTS.md content.
13
+ *
14
+ * install-skill appends a block of the shape:
15
+ *
16
+ * \n\n## API Mapper MCP\n\n<MARKER_LINE>\n<one descriptive line>\n
17
+ *
18
+ * We remove the `## API Mapper MCP` heading (and any leading blank lines
19
+ * before it) through the descriptive line that follows the marker. The match
20
+ * is anchored on MARKER_LINE so an edited heading still gets cleaned as long
21
+ * as the marker is intact. Returns the cleaned text, or `null` if no marker
22
+ * was present (caller treats that as "nothing removed").
23
+ */
24
+ export function stripMarkerBlock(content) {
25
+ if (!content.includes(MARKER_LINE))
26
+ return null;
27
+ const lines = content.split("\n");
28
+ const markerIdx = lines.findIndex((l) => l.trim() === MARKER_LINE.trim());
29
+ if (markerIdx === -1) {
30
+ // Marker present inline but not on its own line — fall back to a plain
31
+ // substring strip of the marker line itself.
32
+ return content.split(MARKER_LINE).join("");
33
+ }
34
+ // Walk backwards to include the `## API Mapper MCP` heading and the blank
35
+ // lines that separated it from the previous content.
36
+ let start = markerIdx;
37
+ // Skip a blank line directly above the marker (between heading and marker).
38
+ if (start > 0 && lines[start - 1].trim() === "")
39
+ start -= 1;
40
+ // Include the heading line if it is the install-skill heading.
41
+ if (start > 0 && lines[start - 1].trim() === "## API Mapper MCP")
42
+ start -= 1;
43
+ // Swallow leading blank separator lines above the heading.
44
+ while (start > 0 && lines[start - 1].trim() === "")
45
+ start -= 1;
46
+ // Walk forward to include the descriptive line(s) after the marker, up to
47
+ // the next blank line or EOF.
48
+ let end = markerIdx + 1;
49
+ while (end < lines.length && lines[end].trim() !== "")
50
+ end += 1;
51
+ const head = lines.slice(0, start);
52
+ const tail = lines.slice(end);
53
+ let next = [...head, ...tail].join("\n");
54
+ // Normalise: collapse a triple+ newline seam left by the excision and keep
55
+ // a single trailing newline if the original had one.
56
+ next = next.replace(/\n{3,}/g, "\n\n");
57
+ if (content.endsWith("\n") && next.length > 0 && !next.endsWith("\n")) {
58
+ next += "\n";
59
+ }
60
+ return next;
61
+ }
62
+ export async function uninstallSkill(options = {}) {
63
+ const targetSkillsDir = options.targetSkillsDir ?? join(homedir(), ".claude", "skills");
64
+ const targetAgentsFile = options.targetAgentsFile ?? join(homedir(), "AGENTS.md");
65
+ // 1. Remove the skill dir.
66
+ let skillRemoved = false;
67
+ const skillDir = join(targetSkillsDir, "apimapper");
68
+ if (existsSync(skillDir)) {
69
+ rmSync(skillDir, { recursive: true, force: true });
70
+ skillRemoved = true;
71
+ }
72
+ // 2. Strip the marker block from AGENTS.md (do NOT create it if absent).
73
+ let markerRemoved = false;
74
+ if (existsSync(targetAgentsFile)) {
75
+ const existing = readFileSync(targetAgentsFile, "utf8");
76
+ const stripped = stripMarkerBlock(existing);
77
+ if (stripped !== null) {
78
+ writeFileSync(targetAgentsFile, stripped, "utf8");
79
+ markerRemoved = true;
80
+ }
81
+ }
82
+ return { skillRemoved, markerRemoved };
83
+ }
84
+ /** True iff the skill dir is present (drives the default skill-removal prompt). */
85
+ export function isSkillInstalled(targetSkillsDir) {
86
+ const dir = targetSkillsDir ?? join(homedir(), ".claude", "skills");
87
+ return existsSync(join(dir, "apimapper"));
88
+ }
89
+ //# sourceMappingURL=uninstall-skill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall-skill.js","sourceRoot":"","sources":["../src/uninstall-skill.ts"],"names":[],"mappings":"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"}
@@ -11,17 +11,17 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
11
11
  ┌──────────────────────────────────────────────────────────────┐
12
12
  │ AI client │
13
13
  │ (Claude Desktop · Claude Code · Cursor · VS Code · Cline · Codex CLI) │
14
- └─────────────┬─────────────────────────────────┬──────────────┘
15
- │ stdio │ HTTP+OAuth2 PKCE
16
- │ (npx / DXT) │ (server-http.ts)
17
-
14
+ └─────────────────────────────┬────────────────────────────────┘
15
+ │ stdio
16
+ │ (npx / DXT)
17
+
18
18
  ┌──────────────────────────────────────────────────────────────┐
19
19
  │ @wootsup/mcp (this package) │
20
20
  │ │
21
21
  │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
22
22
  │ │ Transport │→ │ Tool │→ │ Platform router │ │
23
- │ │ stdio / │ │ registry │ │ (WP / Joomla) │ │
24
- │ │ HTTP+OAuth │ │ (~76 tools) │ │ │ │
23
+ │ │ stdio │ │ registry │ │ (WP / Joomla) │ │
24
+ │ │ │ │ (79 tools) │ │ │ │
25
25
  │ └──────────────┘ └──────────────┘ └──────────────────┘ │
26
26
  │ │ ▲ │ │
27
27
  │ │ │ │ │
@@ -54,11 +54,9 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
54
54
  ## Five components
55
55
 
56
56
  1. **Transport layer** (`src/transports/`)
57
- - `stdio.ts` — default; pipes JSON-RPC over stdin/stdout. Spawned
58
- by the AI client.
59
- - `http.ts` + `server-http.ts` — HTTP+SSE transport behind an
60
- OAuth 2.0 PKCE-required authorization server. Bearer tokens are
61
- site-bound; cross-origin requests are rejected.
57
+ - `stdio.ts` — the only transport. Pipes JSON-RPC over stdin/stdout.
58
+ Spawned by the AI client (npx / DXT). The server opens no network
59
+ listener.
62
60
 
63
61
  2. **Tool registry** (`src/modules/apimapper/`)
64
62
  - 15 register-files (connections, credentials, flows, graph,
@@ -86,7 +84,7 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
86
84
  5. **Setup + skills** (`src/setup-cli.ts`, `skills/apimapper/`)
87
85
  - `@clack/prompts` interactive wizard.
88
86
  - Auto-detects AI clients, patches their MCP config idempotently.
89
- - Ships the skill files (`SKILL.md` + 4 references) into
87
+ - Ships the skill files (`SKILL.md` + 9 reference docs) into
90
88
  `~/.claude/skills/apimapper/`.
91
89
 
92
90
  ## Auth flow (token issuance)
@@ -105,17 +103,19 @@ internal plan-doc `~/Projekte/getimo/40-Plans/active/
105
103
  At runtime, the server reads the token from the keychain on every
106
104
  launch — tokens never live in the client config file.
107
105
 
108
- ## Multi-transport rationale
106
+ ## Transport rationale
109
107
 
110
- | | stdio | HTTP+OAuth |
111
- |---|---|---|
112
- | Where it runs | User's machine | User's machine or remote |
113
- | Auth | Bearer from keychain | OAuth 2.0 PKCE bound to site |
114
- | Use case | Local dev, single user | Remote AI agents (future) |
115
- | Discovery | Static (one-tool-set) | Dynamic per session |
108
+ The MCP server ships **stdio-only**:
116
109
 
117
- Both paths feed the same tool registry; the only difference is the
118
- transport adapter wired into `createServer()`.
110
+ | | stdio |
111
+ |---|---|
112
+ | Where it runs | User's machine |
113
+ | Auth | Bearer (`amk_*`) from keychain / profile |
114
+ | Use case | Local dev, single user |
115
+ | Discovery | Static (one tool-set) |
116
+
117
+ The AI client spawns the server as a child process and speaks JSON-RPC
118
+ over stdin/stdout; there is no HTTP, SSE, or remote transport.
119
119
 
120
120
  ## Multi-platform rationale
121
121
 
@@ -99,10 +99,10 @@ then delete.
99
99
 
100
100
  ```bash
101
101
  npm view @wootsup/mcp version
102
- # Expect: 0.1.0-rc.1 (or whatever the published tag was)
102
+ # Expect: 0.2.0 (rc.X-wN.M tail retired plain semver now)
103
103
 
104
104
  npm view @wootsup/mcp dist-tags
105
- # Expect: { latest: ..., rc: '0.1.0-rc.1' } for the rc tag
105
+ # Expect: latest now carries 0.2.0 (rc dist-tag track retired)
106
106
  ```
107
107
 
108
108
  3. **Run the setup wizard with the published package**
@@ -154,8 +154,8 @@ then delete.
154
154
  status: ok
155
155
  platform: wordpress (or joomla)
156
156
  site_url: https://dev.wootsup.com/wordpress
157
- plugin_version: 2.0.7
158
- tool_count: 74
157
+ plugin_version: 2.0.13
158
+ tool_count: 79
159
159
  ```
160
160
 
161
161
  8. **Run the full smoke suite**
package/docs/security.md CHANGED
@@ -16,12 +16,12 @@ reporting process and supported-version policy, see
16
16
  | T6 | Confused-deputy LLM (read-only token gets `*_delete` tools listed) | High | Med | Scope filter excludes destructive tools from tool-list when the token lacks the scope. |
17
17
  | T7 | Local-fs token theft (no keychain) | Med | Med | `0600` file mode; keychain default on macOS/Windows; SECURITY.md warns about multi-user environments. |
18
18
  | T8 | DXT supply-chain (malicious .dxt) | Low | Critical | DXT bundle uploaded as a signed GitHub Release asset; `apimapper-mcp-publish.yml` workflow publishes with `--provenance`. |
19
- | T9 | OAuth callback hijack (HTTP transport) | Med | High | PKCE-required (S256); state param checked; loopback callback only. |
19
+ | T9 | Network exposure of the MCP server | n/a | n/a | Not applicable the server is stdio-only and opens no network listener. |
20
20
  | T10 | TOCTOU on rotate (old token works after rotate UI shows success) | Low | Med | Rotation is atomic on the server; the response shows the new token only after the old one is invalidated. |
21
21
 
22
22
  ## Token rotation
23
23
 
24
- - **From admin UI:** API Mapper → Connections → MCP Access → **Rotate**.
24
+ - **From admin UI:** API Mapper → **⋮** menu Settings → MCP Access → **Rotate**.
25
25
  - **From CLI:** `apimapper_credential_*` tools do **not** rotate MCP keys
26
26
  themselves — by design, key rotation is an admin-UI gesture (not a
27
27
  tool the LLM can call) to prevent confused-deputy rotation. Rotation
@@ -68,25 +68,6 @@ signing key. The user-visible site title (rendered by the wizard from
68
68
  the server's response, not the URL bar) gives one more verification
69
69
  surface for the user.
70
70
 
71
- ## OAuth 2.0 PKCE (HTTP transport)
72
-
73
- When `@wootsup/mcp` runs in HTTP mode:
74
-
75
- - **Grants supported:** `authorization_code` with PKCE-S256 only.
76
- - **Implicit grant:** disabled.
77
- - **`code` grant without PKCE:** rejected with `invalid_request`.
78
- - **Loopback callbacks only:** `redirect_uri` must be
79
- `http://127.0.0.1:<port>/callback` or `http://localhost:<port>/callback`.
80
- - **State param:** required and verified.
81
- - **Origin header:** Bearer tokens are bound to the issuing site URL.
82
- A request from a different `Origin` is rejected (`401`).
83
- - **Token TTL:** 1 hour. Refresh tokens are not issued — the client
84
- re-runs the auth flow.
85
-
86
- See `src/server-http.ts` for the implementation and
87
- `src/server-http.test.ts` (2 tests) + `src/transports/http.test.ts`
88
- (10 tests) for the pinning.
89
-
90
71
  ## Keychain fallback security
91
72
 
92
73
  When `@napi-rs/keyring` cannot reach an OS keychain: