@wootsup/mcp 0.1.0-rc.1
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/CHANGELOG.md +91 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/SECURITY.md +163 -0
- package/dist/auth/keychain.d.ts +47 -0
- package/dist/auth/keychain.js +262 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-provider.d.ts +68 -0
- package/dist/auth/oauth-provider.js +232 -0
- package/dist/auth/oauth-provider.js.map +1 -0
- package/dist/auth/profiles.d.ts +52 -0
- package/dist/auth/profiles.js +200 -0
- package/dist/auth/profiles.js.map +1 -0
- package/dist/auth/token.d.ts +27 -0
- package/dist/auth/token.js +88 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/install-skill.d.ts +23 -0
- package/dist/install-skill.js +73 -0
- package/dist/install-skill.js.map +1 -0
- package/dist/modules/apimapper/cache.d.ts +2 -0
- package/dist/modules/apimapper/cache.js +71 -0
- package/dist/modules/apimapper/cache.js.map +1 -0
- package/dist/modules/apimapper/client.d.ts +85 -0
- package/dist/modules/apimapper/client.js +523 -0
- package/dist/modules/apimapper/client.js.map +1 -0
- package/dist/modules/apimapper/connections.d.ts +2 -0
- package/dist/modules/apimapper/connections.js +406 -0
- package/dist/modules/apimapper/connections.js.map +1 -0
- package/dist/modules/apimapper/credential-sanitizer.d.ts +7 -0
- package/dist/modules/apimapper/credential-sanitizer.js +70 -0
- package/dist/modules/apimapper/credential-sanitizer.js.map +1 -0
- package/dist/modules/apimapper/credentials.d.ts +2 -0
- package/dist/modules/apimapper/credentials.js +258 -0
- package/dist/modules/apimapper/credentials.js.map +1 -0
- package/dist/modules/apimapper/diagnose.d.ts +18 -0
- package/dist/modules/apimapper/diagnose.js +305 -0
- package/dist/modules/apimapper/diagnose.js.map +1 -0
- package/dist/modules/apimapper/flows.d.ts +2 -0
- package/dist/modules/apimapper/flows.js +372 -0
- package/dist/modules/apimapper/flows.js.map +1 -0
- package/dist/modules/apimapper/get-skill.d.ts +4 -0
- package/dist/modules/apimapper/get-skill.js +88 -0
- package/dist/modules/apimapper/get-skill.js.map +1 -0
- package/dist/modules/apimapper/graph-builder.d.ts +47 -0
- package/dist/modules/apimapper/graph-builder.js +117 -0
- package/dist/modules/apimapper/graph-builder.js.map +1 -0
- package/dist/modules/apimapper/graph.d.ts +2 -0
- package/dist/modules/apimapper/graph.js +117 -0
- package/dist/modules/apimapper/graph.js.map +1 -0
- package/dist/modules/apimapper/index.d.ts +2 -0
- package/dist/modules/apimapper/index.js +43 -0
- package/dist/modules/apimapper/index.js.map +1 -0
- package/dist/modules/apimapper/inspect.d.ts +20 -0
- package/dist/modules/apimapper/inspect.js +86 -0
- package/dist/modules/apimapper/inspect.js.map +1 -0
- package/dist/modules/apimapper/library.d.ts +2 -0
- package/dist/modules/apimapper/library.js +237 -0
- package/dist/modules/apimapper/library.js.map +1 -0
- package/dist/modules/apimapper/license.d.ts +2 -0
- package/dist/modules/apimapper/license.js +142 -0
- package/dist/modules/apimapper/license.js.map +1 -0
- package/dist/modules/apimapper/local-sources.d.ts +2 -0
- package/dist/modules/apimapper/local-sources.js +123 -0
- package/dist/modules/apimapper/local-sources.js.map +1 -0
- package/dist/modules/apimapper/misc.d.ts +2 -0
- package/dist/modules/apimapper/misc.js +149 -0
- package/dist/modules/apimapper/misc.js.map +1 -0
- package/dist/modules/apimapper/node-schema.d.ts +217 -0
- package/dist/modules/apimapper/node-schema.js +218 -0
- package/dist/modules/apimapper/node-schema.js.map +1 -0
- package/dist/modules/apimapper/normalizers.d.ts +13 -0
- package/dist/modules/apimapper/normalizers.js +37 -0
- package/dist/modules/apimapper/normalizers.js.map +1 -0
- package/dist/modules/apimapper/onboarding.d.ts +51 -0
- package/dist/modules/apimapper/onboarding.js +201 -0
- package/dist/modules/apimapper/onboarding.js.map +1 -0
- package/dist/modules/apimapper/schema.d.ts +2 -0
- package/dist/modules/apimapper/schema.js +84 -0
- package/dist/modules/apimapper/schema.js.map +1 -0
- package/dist/modules/apimapper/settings.d.ts +2 -0
- package/dist/modules/apimapper/settings.js +157 -0
- package/dist/modules/apimapper/settings.js.map +1 -0
- package/dist/modules/apimapper/skill-resources.d.ts +4 -0
- package/dist/modules/apimapper/skill-resources.js +85 -0
- package/dist/modules/apimapper/skill-resources.js.map +1 -0
- package/dist/modules/apimapper/types.d.ts +111 -0
- package/dist/modules/apimapper/types.js +14 -0
- package/dist/modules/apimapper/types.js.map +1 -0
- package/dist/modules/apimapper/use-profile.d.ts +34 -0
- package/dist/modules/apimapper/use-profile.js +176 -0
- package/dist/modules/apimapper/use-profile.js.map +1 -0
- package/dist/modules/apimapper/workflows.d.ts +2 -0
- package/dist/modules/apimapper/workflows.js +301 -0
- package/dist/modules/apimapper/workflows.js.map +1 -0
- package/dist/platform/index.d.ts +71 -0
- package/dist/platform/index.js +377 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/server-http.d.ts +22 -0
- package/dist/server-http.js +159 -0
- package/dist/server-http.js.map +1 -0
- package/dist/setup/detect-clients.d.ts +39 -0
- package/dist/setup/detect-clients.js +152 -0
- package/dist/setup/detect-clients.js.map +1 -0
- package/dist/setup/probe-handshake.d.ts +26 -0
- package/dist/setup/probe-handshake.js +159 -0
- package/dist/setup/probe-handshake.js.map +1 -0
- package/dist/setup/write-config.d.ts +25 -0
- package/dist/setup/write-config.js +247 -0
- package/dist/setup/write-config.js.map +1 -0
- package/dist/setup-cli.d.ts +49 -0
- package/dist/setup-cli.js +292 -0
- package/dist/setup-cli.js.map +1 -0
- package/dist/skill-instructions.d.ts +10 -0
- package/dist/skill-instructions.js +68 -0
- package/dist/skill-instructions.js.map +1 -0
- package/dist/transports/http.d.ts +29 -0
- package/dist/transports/http.js +267 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +9 -0
- package/dist/transports/stdio.js +19 -0
- package/dist/transports/stdio.js.map +1 -0
- package/docs/architecture.md +140 -0
- package/docs/customgraph-internal-migration.md +210 -0
- package/docs/security.md +126 -0
- package/docs/tools.md +230 -0
- package/manifest.json +76 -0
- package/package.json +61 -0
- package/skills/apimapper/SKILL.md +57 -0
- package/skills/apimapper/reference/joomla.md +85 -0
- package/skills/apimapper/reference/oauth.md +94 -0
- package/skills/apimapper/reference/troubleshooting.md +123 -0
- package/skills/apimapper/reference/yootheme.md +96 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/auth/profiles.ts — Phase 5.2.
|
|
2
|
+
//
|
|
3
|
+
// Flat-file profile registry for the API Mapper MCP server. A "profile"
|
|
4
|
+
// pairs a site URL + platform kind with a Keychain ref that points to the
|
|
5
|
+
// raw token. Tokens themselves NEVER live in this file — the Keychain is
|
|
6
|
+
// the single source of truth for secret material.
|
|
7
|
+
//
|
|
8
|
+
// Storage layout under <configDir>:
|
|
9
|
+
// profiles.json — JSON array of Profile records.
|
|
10
|
+
// active — single line containing the active profile name (or absent).
|
|
11
|
+
//
|
|
12
|
+
// Active pointer is split out so a torn write to profiles.json doesn't take
|
|
13
|
+
// down "which profile am I using right now".
|
|
14
|
+
import { mkdirSync, existsSync, readFileSync, writeFileSync, unlinkSync, } from "node:fs";
|
|
15
|
+
import { homedir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
// ── Standard OS config dir resolution ────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the canonical config directory for apimapper-mcp:
|
|
20
|
+
*
|
|
21
|
+
* - macOS/Linux: $XDG_CONFIG_HOME/apimapper-mcp/ if set, else
|
|
22
|
+
* $HOME/.config/apimapper-mcp/
|
|
23
|
+
* - Windows: %APPDATA%/apimapper-mcp/
|
|
24
|
+
*
|
|
25
|
+
* Falls back to $HOME/.apimapper-mcp/ if none of the env vars are set
|
|
26
|
+
* (degenerate environment — better than failing).
|
|
27
|
+
*/
|
|
28
|
+
export function resolveConfigDir() {
|
|
29
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
30
|
+
if (xdg && xdg.length > 0)
|
|
31
|
+
return join(xdg, "apimapper-mcp");
|
|
32
|
+
if (process.platform === "win32") {
|
|
33
|
+
const appdata = process.env.APPDATA;
|
|
34
|
+
if (appdata && appdata.length > 0)
|
|
35
|
+
return join(appdata, "apimapper-mcp");
|
|
36
|
+
}
|
|
37
|
+
const home = homedir();
|
|
38
|
+
if (home && home.length > 0) {
|
|
39
|
+
return join(home, ".config", "apimapper-mcp");
|
|
40
|
+
}
|
|
41
|
+
return join(process.cwd(), ".apimapper-mcp");
|
|
42
|
+
}
|
|
43
|
+
// ── ProfileStore ─────────────────────────────────────────────────────────
|
|
44
|
+
export class ProfileStore {
|
|
45
|
+
dir;
|
|
46
|
+
profilesPath;
|
|
47
|
+
activePath;
|
|
48
|
+
constructor(configDir = resolveConfigDir()) {
|
|
49
|
+
this.dir = configDir;
|
|
50
|
+
this.profilesPath = join(configDir, "profiles.json");
|
|
51
|
+
this.activePath = join(configDir, "active");
|
|
52
|
+
if (!existsSync(this.dir)) {
|
|
53
|
+
mkdirSync(this.dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ── reads ──────────────────────────────────────────────────────────────
|
|
57
|
+
readAll() {
|
|
58
|
+
if (!existsSync(this.profilesPath))
|
|
59
|
+
return [];
|
|
60
|
+
let raw;
|
|
61
|
+
try {
|
|
62
|
+
raw = readFileSync(this.profilesPath, "utf8");
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
if (!raw.trim())
|
|
68
|
+
return [];
|
|
69
|
+
let parsed;
|
|
70
|
+
try {
|
|
71
|
+
parsed = JSON.parse(raw);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
if (!Array.isArray(parsed))
|
|
77
|
+
return [];
|
|
78
|
+
// Filter out malformed entries rather than throwing — a corrupt single
|
|
79
|
+
// record shouldn't take down the whole store.
|
|
80
|
+
return parsed.filter(isProfile);
|
|
81
|
+
}
|
|
82
|
+
writeAll(list) {
|
|
83
|
+
writeFileSync(this.profilesPath, JSON.stringify(list, null, 2), {
|
|
84
|
+
mode: 0o600,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
readActiveName() {
|
|
88
|
+
if (!existsSync(this.activePath))
|
|
89
|
+
return null;
|
|
90
|
+
try {
|
|
91
|
+
const raw = readFileSync(this.activePath, "utf8").trim();
|
|
92
|
+
return raw.length > 0 ? raw : null;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
writeActiveName(name) {
|
|
99
|
+
if (name === null) {
|
|
100
|
+
if (existsSync(this.activePath)) {
|
|
101
|
+
try {
|
|
102
|
+
unlinkSync(this.activePath);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* best-effort */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
writeFileSync(this.activePath, name, { mode: 0o600 });
|
|
111
|
+
}
|
|
112
|
+
// ── public API ─────────────────────────────────────────────────────────
|
|
113
|
+
async list() {
|
|
114
|
+
return this.readAll();
|
|
115
|
+
}
|
|
116
|
+
async get(name) {
|
|
117
|
+
const all = this.readAll();
|
|
118
|
+
return all.find((p) => p.name === name) ?? null;
|
|
119
|
+
}
|
|
120
|
+
async add(profile) {
|
|
121
|
+
const all = this.readAll();
|
|
122
|
+
if (all.some((p) => p.name === profile.name)) {
|
|
123
|
+
throw new Error(`Profile "${profile.name}" already exists`);
|
|
124
|
+
}
|
|
125
|
+
all.push(profile);
|
|
126
|
+
this.writeAll(all);
|
|
127
|
+
// Auto-promote the first profile to active. We check the on-disk pointer
|
|
128
|
+
// rather than `all.length === 1` so an externally-cleared active file
|
|
129
|
+
// doesn't accidentally leave the store with profiles but no active one.
|
|
130
|
+
if (this.readActiveName() === null) {
|
|
131
|
+
this.writeActiveName(profile.name);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async remove(name) {
|
|
135
|
+
const all = this.readAll();
|
|
136
|
+
const idx = all.findIndex((p) => p.name === name);
|
|
137
|
+
if (idx < 0)
|
|
138
|
+
return false;
|
|
139
|
+
all.splice(idx, 1);
|
|
140
|
+
this.writeAll(all);
|
|
141
|
+
if (this.readActiveName() === name) {
|
|
142
|
+
// Active profile is gone; clear pointer rather than silently picking a
|
|
143
|
+
// new one. Caller must explicitly setActive() to choose a successor.
|
|
144
|
+
this.writeActiveName(null);
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
async update(name, partial) {
|
|
149
|
+
const all = this.readAll();
|
|
150
|
+
const idx = all.findIndex((p) => p.name === name);
|
|
151
|
+
if (idx < 0)
|
|
152
|
+
throw new Error(`Profile "${name}" not found`);
|
|
153
|
+
// Refuse to rewrite the immutable identity fields via update() — caller
|
|
154
|
+
// should remove + add for that to keep the active pointer + keychain ref
|
|
155
|
+
// story consistent.
|
|
156
|
+
const merged = {
|
|
157
|
+
...all[idx],
|
|
158
|
+
...partial,
|
|
159
|
+
name: all[idx].name,
|
|
160
|
+
addedAt: all[idx].addedAt,
|
|
161
|
+
};
|
|
162
|
+
all[idx] = merged;
|
|
163
|
+
this.writeAll(all);
|
|
164
|
+
}
|
|
165
|
+
async setActive(name) {
|
|
166
|
+
const all = this.readAll();
|
|
167
|
+
if (!all.some((p) => p.name === name)) {
|
|
168
|
+
throw new Error(`Profile "${name}" not found`);
|
|
169
|
+
}
|
|
170
|
+
this.writeActiveName(name);
|
|
171
|
+
}
|
|
172
|
+
async getActive() {
|
|
173
|
+
const name = this.readActiveName();
|
|
174
|
+
if (!name)
|
|
175
|
+
return null;
|
|
176
|
+
const all = this.readAll();
|
|
177
|
+
return all.find((p) => p.name === name) ?? null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ── guards ───────────────────────────────────────────────────────────────
|
|
181
|
+
function isProfile(v) {
|
|
182
|
+
if (!v || typeof v !== "object")
|
|
183
|
+
return false;
|
|
184
|
+
const o = v;
|
|
185
|
+
if (typeof o.name !== "string")
|
|
186
|
+
return false;
|
|
187
|
+
if (typeof o.siteUrl !== "string")
|
|
188
|
+
return false;
|
|
189
|
+
if (o.platform !== "wordpress" &&
|
|
190
|
+
o.platform !== "joomla" &&
|
|
191
|
+
o.platform !== "standalone") {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
if (typeof o.keychainRef !== "string")
|
|
195
|
+
return false;
|
|
196
|
+
if (typeof o.addedAt !== "string")
|
|
197
|
+
return false;
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../src/auth/profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface TokenPayload {
|
|
2
|
+
/** Schema version — currently always 1. */
|
|
3
|
+
v: 1;
|
|
4
|
+
/** Issuer — typically the site URL (e.g. "https://example.com/wp"). */
|
|
5
|
+
iss: string;
|
|
6
|
+
/** Key ID — server-assigned identifier for this key (e.g. "key_abc"). */
|
|
7
|
+
kid: string;
|
|
8
|
+
/** Subject — user ID this key was issued to. */
|
|
9
|
+
sub: string;
|
|
10
|
+
/** Scope — list of permissions granted (e.g. ["read", "admin"]). */
|
|
11
|
+
scope: string[];
|
|
12
|
+
/** Issued-at (unix seconds). */
|
|
13
|
+
iat: number;
|
|
14
|
+
/** Expiry (unix seconds). Optional — keys can be non-expiring. */
|
|
15
|
+
exp?: number;
|
|
16
|
+
/** Human-readable label set by the user at key creation time. */
|
|
17
|
+
name?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Decode a token payload without verifying the signature. Returns the
|
|
21
|
+
* TokenPayload on success, or `null` on ANY failure (malformed format,
|
|
22
|
+
* invalid base64, non-JSON, non-object payload, missing required fields,
|
|
23
|
+
* unsupported schema version).
|
|
24
|
+
*
|
|
25
|
+
* No network call is made.
|
|
26
|
+
*/
|
|
27
|
+
export declare function decodeToken(token: string): TokenPayload | null;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/auth/token.ts — client-side, network-free token payload decode.
|
|
2
|
+
//
|
|
3
|
+
// Token format (matches src/modules/core/src/Mcp/KeyService.php):
|
|
4
|
+
//
|
|
5
|
+
// amk_<env>_<payloadB64url>.<sigB64url>
|
|
6
|
+
//
|
|
7
|
+
// where env ∈ {live, test} and payloadB64url is a JSON object with the
|
|
8
|
+
// shape declared by TokenPayload below.
|
|
9
|
+
//
|
|
10
|
+
// IMPORTANT: this module deliberately does NOT verify the HMAC signature.
|
|
11
|
+
// The signing secret never leaves the PHP plugin; client-side decode is
|
|
12
|
+
// purely informational (display issuer, scope, expiry in setup CLI, etc.).
|
|
13
|
+
// Every actual authorisation decision happens server-side on each request.
|
|
14
|
+
// Token layout: amk_(live|test)_<payloadB64url>.<sigB64url>
|
|
15
|
+
// b64url alphabet is [A-Za-z0-9_-] (no padding).
|
|
16
|
+
const TOKEN_RE = /^amk_(live|test)_([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)$/;
|
|
17
|
+
/** Decode a base64url-encoded string to UTF-8. Returns null on invalid b64. */
|
|
18
|
+
function b64urlDecode(s) {
|
|
19
|
+
try {
|
|
20
|
+
const padded = s.replaceAll("-", "+").replaceAll("_", "/");
|
|
21
|
+
// Add back stripped '=' padding (length must be multiple of 4).
|
|
22
|
+
const padLen = (4 - (padded.length % 4)) % 4;
|
|
23
|
+
const buf = Buffer.from(padded + "=".repeat(padLen), "base64");
|
|
24
|
+
return buf.toString("utf8");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Decode a token payload without verifying the signature. Returns the
|
|
32
|
+
* TokenPayload on success, or `null` on ANY failure (malformed format,
|
|
33
|
+
* invalid base64, non-JSON, non-object payload, missing required fields,
|
|
34
|
+
* unsupported schema version).
|
|
35
|
+
*
|
|
36
|
+
* No network call is made.
|
|
37
|
+
*/
|
|
38
|
+
export function decodeToken(token) {
|
|
39
|
+
if (typeof token !== "string" || token.length === 0)
|
|
40
|
+
return null;
|
|
41
|
+
const m = TOKEN_RE.exec(token);
|
|
42
|
+
if (!m)
|
|
43
|
+
return null;
|
|
44
|
+
const payloadB64 = m[2];
|
|
45
|
+
const rawJson = b64urlDecode(payloadB64);
|
|
46
|
+
if (rawJson === null)
|
|
47
|
+
return null;
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(rawJson);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const p = parsed;
|
|
59
|
+
// Schema-version guard — refuse anything we don't recognise.
|
|
60
|
+
if (p.v !== 1)
|
|
61
|
+
return null;
|
|
62
|
+
// Required fields with the right runtime types.
|
|
63
|
+
if (typeof p.iss !== "string")
|
|
64
|
+
return null;
|
|
65
|
+
if (typeof p.kid !== "string")
|
|
66
|
+
return null;
|
|
67
|
+
if (typeof p.sub !== "string")
|
|
68
|
+
return null;
|
|
69
|
+
if (typeof p.iat !== "number")
|
|
70
|
+
return null;
|
|
71
|
+
if (!Array.isArray(p.scope) || !p.scope.every((s) => typeof s === "string")) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const out = {
|
|
75
|
+
v: 1,
|
|
76
|
+
iss: p.iss,
|
|
77
|
+
kid: p.kid,
|
|
78
|
+
sub: p.sub,
|
|
79
|
+
scope: p.scope,
|
|
80
|
+
iat: p.iat,
|
|
81
|
+
};
|
|
82
|
+
if (typeof p.exp === "number")
|
|
83
|
+
out.exp = p.exp;
|
|
84
|
+
if (typeof p.name === "string")
|
|
85
|
+
out.name = p.name;
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/auth/token.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
/**
|
|
4
|
+
* Constructs the McpServer with bundled skill instructions wired into the
|
|
5
|
+
* `instructions` field. Extracted as a factory for testability.
|
|
6
|
+
*
|
|
7
|
+
* The instructions string is sent to MCP clients (Claude / ChatGPT / Cursor)
|
|
8
|
+
* as system-prompt context on every session — see Phase 7.2 of the
|
|
9
|
+
* apimapper-mcp implementation plan.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createServer(): McpServer;
|
|
12
|
+
declare const server: McpServer;
|
|
13
|
+
export { server };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @customgraph/mcp-adapter-apimapper-rest — API Mapper REST MCP Server
|
|
3
|
+
// Full coverage of /wp-json/api-mapper/v1/* (~65 tools across 12 modules)
|
|
4
|
+
//
|
|
5
|
+
// Auth: WordPress Application Password (1Password Claude-Secrets vault) or
|
|
6
|
+
// API Mapper MCP-key (amk_live_...) for DXT/GUI installs.
|
|
7
|
+
// See README.md for setup + smoke-test.
|
|
8
|
+
// ── DXT environment bootstrap (Phase 9.3) ─────────────────────────────
|
|
9
|
+
// When installed via Claude Desktop's DXT bundle path, the runtime is
|
|
10
|
+
// invoked with three user_config env vars:
|
|
11
|
+
// APIMAPPER_TOKEN — amk_live_... MCP key
|
|
12
|
+
// APIMAPPER_SITE_URL — site base URL (https://example.com)
|
|
13
|
+
// APIMAPPER_PLATFORM — "wordpress" | "joomla"
|
|
14
|
+
// The existing client.ts module reads legacy APIMAPPER_WP_* vars — we
|
|
15
|
+
// bridge here so both setup paths work without duplicating wiring.
|
|
16
|
+
// This MUST happen before any module imports that read process.env.
|
|
17
|
+
if (process.env.APIMAPPER_TOKEN && !process.env.APIMAPPER_WP_APP_PASS) {
|
|
18
|
+
process.env.APIMAPPER_WP_APP_PASS = process.env.APIMAPPER_TOKEN;
|
|
19
|
+
}
|
|
20
|
+
if (process.env.APIMAPPER_SITE_URL && !process.env.APIMAPPER_WP_BASE) {
|
|
21
|
+
process.env.APIMAPPER_WP_BASE = process.env.APIMAPPER_SITE_URL;
|
|
22
|
+
}
|
|
23
|
+
// APIMAPPER_PLATFORM is consumed by the platform factory once wired
|
|
24
|
+
// (Phase 4 abstraction lands per-tool). Surfaced as a no-op read here so
|
|
25
|
+
// the build-dxt grep gate verifies every user_config key has a runtime
|
|
26
|
+
// consumer — see scripts/build-dxt.js.
|
|
27
|
+
void process.env.APIMAPPER_PLATFORM;
|
|
28
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
29
|
+
import { loadModules, formatResult, readOnly } from "@getimo/mcp-toolkit";
|
|
30
|
+
import { apimapperRestModule } from "./modules/apimapper/index.js";
|
|
31
|
+
import { loadSkillInstructions } from "./skill-instructions.js";
|
|
32
|
+
import { connectStdio } from "./transports/stdio.js";
|
|
33
|
+
import { createKeychain } from "./auth/keychain.js";
|
|
34
|
+
import { ProfileStore, resolveConfigDir } from "./auth/profiles.js";
|
|
35
|
+
import { registerUseProfileTool } from "./modules/apimapper/use-profile.js";
|
|
36
|
+
/**
|
|
37
|
+
* Constructs the McpServer with bundled skill instructions wired into the
|
|
38
|
+
* `instructions` field. Extracted as a factory for testability.
|
|
39
|
+
*
|
|
40
|
+
* The instructions string is sent to MCP clients (Claude / ChatGPT / Cursor)
|
|
41
|
+
* as system-prompt context on every session — see Phase 7.2 of the
|
|
42
|
+
* apimapper-mcp implementation plan.
|
|
43
|
+
*/
|
|
44
|
+
export function createServer() {
|
|
45
|
+
return new McpServer({
|
|
46
|
+
name: "apimapper-rest",
|
|
47
|
+
version: "0.2.0",
|
|
48
|
+
}, {
|
|
49
|
+
instructions: loadSkillInstructions(),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const server = createServer();
|
|
53
|
+
let moduleStatuses = [];
|
|
54
|
+
// Root tool: module-loading status. Distinct from `apimapper_health` (which
|
|
55
|
+
// probes connectivity + auth against the live REST surface). Names diverge
|
|
56
|
+
// to avoid agent confusion.
|
|
57
|
+
server.registerTool("apimapper_rest_modules_status", {
|
|
58
|
+
title: "Adapter Modules Status",
|
|
59
|
+
description: "Module-loading status for this MCP adapter (which sub-modules registered cleanly). " +
|
|
60
|
+
"For runtime connectivity + auth checks against the API Mapper REST surface, use apimapper_health.",
|
|
61
|
+
inputSchema: {},
|
|
62
|
+
annotations: readOnly(),
|
|
63
|
+
}, async () => {
|
|
64
|
+
const ok = moduleStatuses.filter((m) => m.status === "ok").length;
|
|
65
|
+
const failed = moduleStatuses.filter((m) => m.status === "error").length;
|
|
66
|
+
return formatResult({
|
|
67
|
+
version: "0.2.0",
|
|
68
|
+
modules_loaded: ok,
|
|
69
|
+
modules_total: moduleStatuses.length,
|
|
70
|
+
modules_failed: failed,
|
|
71
|
+
modules: moduleStatuses.map((m) => ({
|
|
72
|
+
name: m.name,
|
|
73
|
+
status: m.status,
|
|
74
|
+
description: m.description,
|
|
75
|
+
error: m.error,
|
|
76
|
+
})),
|
|
77
|
+
next: "Use apimapper_health to verify live connectivity + auth.",
|
|
78
|
+
}, false, { maxChars: 2000 });
|
|
79
|
+
});
|
|
80
|
+
async function main() {
|
|
81
|
+
moduleStatuses = await loadModules(server, [apimapperRestModule]);
|
|
82
|
+
// Profile + keychain wiring for apimapper_use_profile.
|
|
83
|
+
// Phase 5 deferred this to the server entrypoint because constructing the
|
|
84
|
+
// keychain is async (it probes the OS Secret-Service, falling back to a
|
|
85
|
+
// file impl) and the module-level register() signature is sync.
|
|
86
|
+
// Best-effort: log + skip if the keychain init throws on a locked-down host
|
|
87
|
+
// so the rest of the MCP surface still boots.
|
|
88
|
+
try {
|
|
89
|
+
const configDir = resolveConfigDir();
|
|
90
|
+
const keychain = await createKeychain({ configDir });
|
|
91
|
+
const profileStore = new ProfileStore(configDir);
|
|
92
|
+
registerUseProfileTool(server, { store: profileStore, keychain });
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.error("[apimapper-rest] use-profile tool disabled — keychain/profile init failed:", err instanceof Error ? err.message : err);
|
|
96
|
+
}
|
|
97
|
+
process.stdout.on("error", (err) => {
|
|
98
|
+
if (err.code === "EPIPE" || err.code === "ERR_STREAM_DESTROYED")
|
|
99
|
+
process.exit(0);
|
|
100
|
+
throw err;
|
|
101
|
+
});
|
|
102
|
+
await connectStdio(server);
|
|
103
|
+
const ok = moduleStatuses.filter((m) => m.status === "ok").length;
|
|
104
|
+
console.error(`[apimapper-rest] MCP Server v0.2.0 running — ${ok}/${moduleStatuses.length} modules loaded`);
|
|
105
|
+
}
|
|
106
|
+
// ── Subcommand dispatch (Phase 6 setup CLI integration) ──────────────
|
|
107
|
+
// AI clients invoke `apimapper-mcp` with stdio piped and no subcommand;
|
|
108
|
+
// in that case we boot the MCP server (default branch below).
|
|
109
|
+
// Humans run `apimapper-mcp setup` / `install-skill` / `help` from a
|
|
110
|
+
// terminal — those subcommands route to setup-cli.ts's runCli().
|
|
111
|
+
//
|
|
112
|
+
// Setup-cli also handles wizard/help/install-skill flows. We import it
|
|
113
|
+
// lazily inside the subcommand branch so the AI-client startup path
|
|
114
|
+
// stays free of clack/prompts side-effects.
|
|
115
|
+
const subcommand = process.argv[2];
|
|
116
|
+
const CLI_SUBCOMMANDS = new Set(["setup", "install-skill", "install", "help", "--help", "-h"]);
|
|
117
|
+
if (subcommand && CLI_SUBCOMMANDS.has(subcommand)) {
|
|
118
|
+
void (async () => {
|
|
119
|
+
try {
|
|
120
|
+
const { runCli } = await import("./setup-cli.js");
|
|
121
|
+
const code = await runCli(process.argv.slice(2));
|
|
122
|
+
process.exit(code);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
console.error("[apimapper-mcp] CLI fatal error:", err);
|
|
126
|
+
process.exit(99);
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
main().catch((error) => {
|
|
132
|
+
console.error("[apimapper-rest] Fatal error:", error);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
export { server };
|
|
137
|
+
//# 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,23 @@
|
|
|
1
|
+
export declare const MARKER_LINE = "<!-- apimapper-mcp:skill-installed -->";
|
|
2
|
+
export interface InstallSkillOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Package root that contains the bundled `skills/` dir. Defaults to the
|
|
5
|
+
* directory two levels up from this module (i.e. the package root in
|
|
6
|
+
* both dev and dist layouts).
|
|
7
|
+
*/
|
|
8
|
+
pkgRoot?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Target dir where the `apimapper/` skill folder will land.
|
|
11
|
+
* Default: ~/.claude/skills/
|
|
12
|
+
*/
|
|
13
|
+
targetSkillsDir?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Target AGENTS.md file to append the marker to. Default: ~/AGENTS.md.
|
|
16
|
+
*/
|
|
17
|
+
targetAgentsFile?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface InstallSkillResult {
|
|
20
|
+
copied: boolean;
|
|
21
|
+
markerAlreadyPresent: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function installSkill(options?: InstallSkillOptions): Promise<InstallSkillResult>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/install-skill.ts — Phase 7.5.
|
|
2
|
+
//
|
|
3
|
+
// installSkill() copies the bundled skills/apimapper/ directory into the
|
|
4
|
+
// caller's skills directory (typically ~/.claude/skills/) and appends a
|
|
5
|
+
// marker line to an AGENTS.md file (typically ~/AGENTS.md). Idempotent.
|
|
6
|
+
//
|
|
7
|
+
// Called from the CLI by `apimapper-mcp install-skill`. Phase 6's @clack
|
|
8
|
+
// wizard will wrap this for first-run setup; in the meantime, the
|
|
9
|
+
// subcommand alone is enough for users to point their AI client at the
|
|
10
|
+
// bundled skill content.
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, copyFileSync, statSync, writeFileSync, appendFileSync, } from "node:fs";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { dirname, join, resolve } from "node:path";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
export const MARKER_LINE = "<!-- apimapper-mcp:skill-installed -->";
|
|
16
|
+
const MARKER_BLOCK = `
|
|
17
|
+
|
|
18
|
+
## API Mapper MCP
|
|
19
|
+
|
|
20
|
+
${MARKER_LINE}
|
|
21
|
+
The API Mapper skill is installed at \`~/.claude/skills/apimapper/\`. Refer to it for flow, connection, OAuth, YOOtheme, and Joomla guidance. Source: https://github.com/wootsup/api-mapper.
|
|
22
|
+
`;
|
|
23
|
+
function defaultPkgRoot() {
|
|
24
|
+
// <pkg-root>/src/install-skill.ts → up one.
|
|
25
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
return resolve(here, "..");
|
|
27
|
+
}
|
|
28
|
+
function copyDirRecursive(src, dst) {
|
|
29
|
+
if (!existsSync(dst))
|
|
30
|
+
mkdirSync(dst, { recursive: true });
|
|
31
|
+
for (const entry of readdirSync(src)) {
|
|
32
|
+
const sPath = join(src, entry);
|
|
33
|
+
const dPath = join(dst, entry);
|
|
34
|
+
const stat = statSync(sPath);
|
|
35
|
+
if (stat.isDirectory()) {
|
|
36
|
+
copyDirRecursive(sPath, dPath);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
copyFileSync(sPath, dPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function installSkill(options = {}) {
|
|
44
|
+
const pkgRoot = options.pkgRoot ?? defaultPkgRoot();
|
|
45
|
+
const targetSkillsDir = options.targetSkillsDir ?? join(homedir(), ".claude", "skills");
|
|
46
|
+
const targetAgentsFile = options.targetAgentsFile ?? join(homedir(), "AGENTS.md");
|
|
47
|
+
// 1. Copy skills/apimapper → targetSkillsDir/apimapper.
|
|
48
|
+
const srcSkillDir = join(pkgRoot, "skills", "apimapper");
|
|
49
|
+
if (!existsSync(srcSkillDir)) {
|
|
50
|
+
throw new Error(`Bundled skill not found at ${srcSkillDir}. ` +
|
|
51
|
+
`Is the package installed correctly? (Expected pkgRoot=${pkgRoot})`);
|
|
52
|
+
}
|
|
53
|
+
if (!existsSync(targetSkillsDir)) {
|
|
54
|
+
mkdirSync(targetSkillsDir, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
copyDirRecursive(srcSkillDir, join(targetSkillsDir, "apimapper"));
|
|
57
|
+
// 2. Append marker to AGENTS.md (idempotent).
|
|
58
|
+
let markerAlreadyPresent = false;
|
|
59
|
+
if (existsSync(targetAgentsFile)) {
|
|
60
|
+
const existing = readFileSync(targetAgentsFile, "utf8");
|
|
61
|
+
if (existing.includes(MARKER_LINE)) {
|
|
62
|
+
markerAlreadyPresent = true;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
appendFileSync(targetAgentsFile, MARKER_BLOCK);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
writeFileSync(targetAgentsFile, MARKER_BLOCK.trimStart(), "utf8");
|
|
70
|
+
}
|
|
71
|
+
return { copied: true, markerAlreadyPresent };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=install-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-skill.js","sourceRoot":"","sources":["../src/install-skill.ts"],"names":[],"mappings":"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"}
|