@aisa-one/cli 0.2.4 → 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.
- package/CHANGELOG.md +29 -0
- package/README.md +12 -7
- package/dist/commands/mcp.d.ts +20 -2
- package/dist/commands/mcp.js +172 -36
- package/dist/constants.d.ts +29 -2
- package/dist/constants.js +37 -3
- package/dist/index.js +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] — 2026-08-18
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`aisa mcp setup` wrote a dead URL.** It hardcoded
|
|
15
|
+
`https://docs.aisa.one/mcp`, a hostname with no DNS record, and wrote it
|
|
16
|
+
into every client's config. Setup now reads the live discovery manifest at
|
|
17
|
+
`aisa.one/.well-known/mcp.json` and writes one entry per live server
|
|
18
|
+
(default set of five; `--all` for everything), so new servers work without
|
|
19
|
+
a CLI release. If the manifest cannot be fetched, nothing is written.
|
|
20
|
+
- **`{url}` entries were written for clients that cannot execute them.**
|
|
21
|
+
Claude Desktop and Windsurf spawn stdio processes and silently ignore url
|
|
22
|
+
entries; they now get an `npx mcp-remote` bridge, verified end to end
|
|
23
|
+
against production.
|
|
24
|
+
- **A config file that failed to parse was replaced with `{}`.** A
|
|
25
|
+
hand-edited `mcp.json` with comments or a trailing comma was wiped by
|
|
26
|
+
setup. It is now left untouched, with an error.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Entries carry the configured API key as a Bearer header; without a key
|
|
31
|
+
they are written credential-less and the server's OAuth challenge drives
|
|
32
|
+
the browser flow on first use.
|
|
33
|
+
- `aisa mcp status` now pings every configured endpoint with a real
|
|
34
|
+
initialize request instead of checking that a JSON key exists — a 401 is
|
|
35
|
+
reported as healthy (the auth challenge), an unresolvable hostname as the
|
|
36
|
+
failure it is.
|
|
37
|
+
- The stale dead entry earlier releases wrote is removed on the next setup.
|
|
38
|
+
|
|
10
39
|
## [0.2.4] — 2026-08-07
|
|
11
40
|
|
|
12
41
|
Text-only follow-up to 0.2.3: no behavioural change, but the URL it printed
|
package/README.md
CHANGED
|
@@ -250,15 +250,20 @@ To publish a skill, open a pull request against
|
|
|
250
250
|
## MCP Server
|
|
251
251
|
|
|
252
252
|
```bash
|
|
253
|
-
aisa mcp setup # configure detected
|
|
254
|
-
aisa mcp
|
|
253
|
+
aisa mcp setup # configure the default servers for every detected client
|
|
254
|
+
aisa mcp setup --all # every live server, not just the defaults
|
|
255
|
+
aisa mcp setup --agent cursor # one client only
|
|
256
|
+
aisa mcp status # list entries and ping each configured endpoint
|
|
255
257
|
```
|
|
256
258
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
`setup` reads the platform's discovery manifest (`aisa.one/.well-known/mcp.json`)
|
|
260
|
+
at run time and writes one entry per live server, in the shape each client
|
|
261
|
+
executes: a `url` entry for Cursor, an `npx mcp-remote` stdio bridge for
|
|
262
|
+
Claude Desktop and Windsurf. With an API key configured the entries carry it
|
|
263
|
+
as a Bearer header; without one they carry no credentials and the server's
|
|
264
|
+
OAuth flow opens in your browser on first use. A config file that exists but
|
|
265
|
+
does not parse is never overwritten. The docs-search MCP is always included
|
|
266
|
+
as `aisa-docs`.
|
|
262
267
|
|
|
263
268
|
## Shell Completion
|
|
264
269
|
|
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
/** One config entry in the shape the client actually executes. */
|
|
2
|
+
export declare function buildEntry(shape: "url" | "stdio", endpoint: string, key: string | undefined): Record<string, unknown>;
|
|
1
3
|
export declare function mcpSetupAction(options: {
|
|
2
4
|
agent?: string;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
all?: boolean;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* `aisa mcp status` — what is configured, and does it actually answer.
|
|
9
|
+
*
|
|
10
|
+
* The old status only checked that a config file contained an "aisa" key,
|
|
11
|
+
* which is exactly how a dead URL looked healthy. Reachability means a real
|
|
12
|
+
* HTTP exchange with each configured endpoint.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mcpStatusAction(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* One initialize POST, no credentials. What each answer means:
|
|
17
|
+
* 200 -> alive and open (the docs MCP)
|
|
18
|
+
* 401 -> alive, auth required — the expected answer from mcp.aisa.one
|
|
19
|
+
* anything else -> alive but odd; say so
|
|
20
|
+
* network error -> the dead-hostname class this command exists to catch
|
|
21
|
+
*/
|
|
22
|
+
export declare function pingEndpoint(endpoint: string): Promise<string>;
|
package/dist/commands/mcp.js
CHANGED
|
@@ -2,19 +2,78 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { success, error, info, hint } from "../utils/display.js";
|
|
4
4
|
import { expandHome, ensureDir } from "../utils/file.js";
|
|
5
|
-
import { MCP_CONFIGS,
|
|
5
|
+
import { MCP_CONFIGS, MCP_MANIFEST_URL, DOCS_MCP_URL, MCP_DEFAULT_SLUGS } from "../constants.js";
|
|
6
|
+
import { getApiKey } from "../config.js";
|
|
6
7
|
import { join } from "node:path";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const stripped = (name) => name.replace(/^AIsa\s+/i, "");
|
|
9
|
+
async function fetchLiveServers() {
|
|
10
|
+
const res = await fetch(MCP_MANIFEST_URL, { signal: AbortSignal.timeout(15_000) });
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new Error(`manifest answered HTTP ${res.status}`);
|
|
13
|
+
const manifest = (await res.json());
|
|
14
|
+
const live = (manifest.servers ?? [])
|
|
15
|
+
.filter((s) => s.status === "live" && s.slug && s.transport?.endpoint)
|
|
16
|
+
.map((s) => ({
|
|
17
|
+
slug: s.slug,
|
|
18
|
+
name: s.name ?? s.slug,
|
|
19
|
+
endpoint: s.transport.endpoint,
|
|
20
|
+
toolCount: s.tools?.length ?? 0,
|
|
21
|
+
}));
|
|
22
|
+
if (live.length === 0)
|
|
23
|
+
throw new Error("manifest lists no live servers");
|
|
24
|
+
return live;
|
|
25
|
+
}
|
|
26
|
+
/** One config entry in the shape the client actually executes. */
|
|
27
|
+
export function buildEntry(shape, endpoint, key) {
|
|
28
|
+
if (shape === "url") {
|
|
29
|
+
return key ? { url: endpoint, headers: { Authorization: `Bearer ${key}` } } : { url: endpoint };
|
|
30
|
+
}
|
|
31
|
+
// The exact invocation verified against production on 2026-08-18. The
|
|
32
|
+
// header value splits on the first colon, so no space after "Authorization:".
|
|
33
|
+
// Without a key, mcp-remote answers the server's 401 challenge by running
|
|
34
|
+
// the OAuth flow itself and caching tokens under ~/.mcp-auth.
|
|
35
|
+
const args = ["-y", "mcp-remote", endpoint];
|
|
36
|
+
if (key)
|
|
37
|
+
args.push("--header", `Authorization:Bearer ${key}`);
|
|
38
|
+
args.push("--transport", "http-only");
|
|
39
|
+
return { command: "npx", args };
|
|
40
|
+
}
|
|
41
|
+
/** The dead entry every pre-v0.3 setup wrote. Remove it on sight. */
|
|
42
|
+
const DEAD_URL = "https://docs.aisa.one/mcp";
|
|
43
|
+
export async function mcpSetupAction(options) {
|
|
44
|
+
const targets = options.agent && options.agent !== "all" ? [options.agent] : Object.keys(MCP_CONFIGS);
|
|
12
45
|
for (const agent of targets) {
|
|
13
|
-
|
|
14
|
-
if (!config) {
|
|
46
|
+
if (!MCP_CONFIGS[agent]) {
|
|
15
47
|
error(`Unknown agent: ${agent}. Valid: ${Object.keys(MCP_CONFIGS).join(", ")}, all`);
|
|
16
48
|
return;
|
|
17
49
|
}
|
|
50
|
+
}
|
|
51
|
+
// Fetch before touching any file: a setup that cannot see the manifest has
|
|
52
|
+
// nothing true to write.
|
|
53
|
+
let servers;
|
|
54
|
+
try {
|
|
55
|
+
servers = await fetchLiveServers();
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
error(`Could not read the MCP manifest at ${MCP_MANIFEST_URL}: ${e.message}`);
|
|
59
|
+
hint("Nothing was written. Check your network and try again.");
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const chosen = options.all
|
|
64
|
+
? servers
|
|
65
|
+
: servers.filter((s) => MCP_DEFAULT_SLUGS.includes(s.slug));
|
|
66
|
+
const skipped = servers.length - chosen.length;
|
|
67
|
+
const key = getApiKey();
|
|
68
|
+
if (key) {
|
|
69
|
+
info("Using your configured API key (Bearer). It will be written into each client's config file.");
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
info("No API key configured — entries are written without credentials, and the");
|
|
73
|
+
info("server's OAuth flow will open in your browser on first use.");
|
|
74
|
+
}
|
|
75
|
+
for (const agent of targets) {
|
|
76
|
+
const config = MCP_CONFIGS[agent];
|
|
18
77
|
const filePath = expandHome(config.path);
|
|
19
78
|
let existing = {};
|
|
20
79
|
if (existsSync(filePath)) {
|
|
@@ -22,46 +81,123 @@ export function mcpSetupAction(options) {
|
|
|
22
81
|
existing = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
23
82
|
}
|
|
24
83
|
catch {
|
|
25
|
-
|
|
84
|
+
// A file we cannot parse is a file we do not own. The old command
|
|
85
|
+
// replaced it with {} — a user's hand-edited config (comments,
|
|
86
|
+
// trailing commas) was wiped by a "setup". Refuse instead.
|
|
87
|
+
error(`${agent}: ${config.path} exists but is not valid JSON — refusing to overwrite it.`);
|
|
88
|
+
hint("Fix or remove the file, then re-run setup.");
|
|
89
|
+
continue;
|
|
26
90
|
}
|
|
27
91
|
}
|
|
28
92
|
else {
|
|
29
93
|
ensureDir(join(filePath, ".."));
|
|
30
94
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
95
|
+
const entries = existing[config.key] || {};
|
|
96
|
+
// Clean up the dead entry earlier releases wrote, and only that one:
|
|
97
|
+
// an "aisa" entry the user pointed somewhere else on purpose stays.
|
|
98
|
+
const stale = entries["aisa"];
|
|
99
|
+
if (stale?.url === DEAD_URL)
|
|
100
|
+
delete entries["aisa"];
|
|
101
|
+
for (const s of chosen) {
|
|
102
|
+
entries[`aisa-${s.slug}`] = buildEntry(config.shape, s.endpoint, key);
|
|
103
|
+
}
|
|
104
|
+
// The docs-search MCP is tiny, unauthenticated, and answers "how do I call
|
|
105
|
+
// this" questions — always included.
|
|
106
|
+
entries["aisa-docs"] = buildEntry(config.shape, DOCS_MCP_URL, undefined);
|
|
107
|
+
existing[config.key] = entries;
|
|
34
108
|
writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
35
|
-
console.log(` ${chalk.green("✓")} ${agent}: ${config.path}`);
|
|
36
|
-
configured++;
|
|
109
|
+
console.log(` ${chalk.green("✓")} ${agent}: ${chosen.length + 1} servers (${config.shape}) → ${config.path}`);
|
|
37
110
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
111
|
+
success(`Configured ${chosen.length} live servers plus docs search for ${targets.length} client(s)`);
|
|
112
|
+
for (const s of chosen) {
|
|
113
|
+
console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)} (${s.toolCount} tools)`);
|
|
41
114
|
}
|
|
115
|
+
if (skipped > 0)
|
|
116
|
+
hint(`${skipped} more live servers available — run with --all to configure every one`);
|
|
117
|
+
hint("Restart your agent/editor to activate");
|
|
42
118
|
}
|
|
43
|
-
|
|
44
|
-
|
|
119
|
+
/**
|
|
120
|
+
* `aisa mcp status` — what is configured, and does it actually answer.
|
|
121
|
+
*
|
|
122
|
+
* The old status only checked that a config file contained an "aisa" key,
|
|
123
|
+
* which is exactly how a dead URL looked healthy. Reachability means a real
|
|
124
|
+
* HTTP exchange with each configured endpoint.
|
|
125
|
+
*/
|
|
126
|
+
export async function mcpStatusAction() {
|
|
127
|
+
const endpoints = new Map(); // endpoint -> [client/entry]
|
|
45
128
|
for (const [agent, config] of Object.entries(MCP_CONFIGS)) {
|
|
46
129
|
const filePath = expandHome(config.path);
|
|
47
|
-
if (existsSync(filePath)) {
|
|
48
|
-
try {
|
|
49
|
-
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
50
|
-
const servers = data[config.key];
|
|
51
|
-
if (servers?.["aisa"]) {
|
|
52
|
-
console.log(` ${chalk.green("✓")} ${agent}: configured`);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
console.log(` ${chalk.gray("○")} ${agent}: not configured`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
console.log(` ${chalk.gray("○")} ${agent}: config unreadable`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
130
|
+
if (!existsSync(filePath)) {
|
|
63
131
|
console.log(` ${chalk.gray("○")} ${agent}: not installed`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
let entries = {};
|
|
135
|
+
try {
|
|
136
|
+
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
137
|
+
entries = data[config.key] || {};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
console.log(` ${chalk.gray("○")} ${agent}: config unreadable`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const ours = Object.entries(entries).filter(([name]) => name.startsWith("aisa"));
|
|
144
|
+
if (ours.length === 0) {
|
|
145
|
+
console.log(` ${chalk.gray("○")} ${agent}: not configured`);
|
|
146
|
+
continue;
|
|
64
147
|
}
|
|
148
|
+
console.log(` ${chalk.green("✓")} ${agent}: ${ours.length} entries`);
|
|
149
|
+
for (const [name, entry] of ours) {
|
|
150
|
+
const e = entry;
|
|
151
|
+
const endpoint = e.url ?? e.args?.find((a) => a.startsWith("http"));
|
|
152
|
+
if (endpoint) {
|
|
153
|
+
const users = endpoints.get(endpoint) ?? [];
|
|
154
|
+
users.push(`${agent}/${name}`);
|
|
155
|
+
endpoints.set(endpoint, users);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (endpoints.size === 0) {
|
|
160
|
+
hint("Run 'aisa mcp setup' to configure");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log("");
|
|
164
|
+
info("Checking each configured endpoint:");
|
|
165
|
+
for (const [endpoint, users] of endpoints) {
|
|
166
|
+
console.log(` ${endpoint} ${chalk.gray(`(${users.length} entr${users.length > 1 ? "ies" : "y"})`)}`);
|
|
167
|
+
console.log(` ${await pingEndpoint(endpoint)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* One initialize POST, no credentials. What each answer means:
|
|
172
|
+
* 200 -> alive and open (the docs MCP)
|
|
173
|
+
* 401 -> alive, auth required — the expected answer from mcp.aisa.one
|
|
174
|
+
* anything else -> alive but odd; say so
|
|
175
|
+
* network error -> the dead-hostname class this command exists to catch
|
|
176
|
+
*/
|
|
177
|
+
export async function pingEndpoint(endpoint) {
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetch(endpoint, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
182
|
+
body: JSON.stringify({
|
|
183
|
+
jsonrpc: "2.0",
|
|
184
|
+
id: 1,
|
|
185
|
+
method: "initialize",
|
|
186
|
+
params: {
|
|
187
|
+
protocolVersion: "2025-06-18",
|
|
188
|
+
capabilities: {},
|
|
189
|
+
clientInfo: { name: "aisa-cli", version: "status" },
|
|
190
|
+
},
|
|
191
|
+
}),
|
|
192
|
+
signal: AbortSignal.timeout(8_000),
|
|
193
|
+
});
|
|
194
|
+
if (res.status === 200)
|
|
195
|
+
return `${chalk.green("✓")} reachable (open)`;
|
|
196
|
+
if (res.status === 401)
|
|
197
|
+
return `${chalk.green("✓")} reachable (auth required — normal)`;
|
|
198
|
+
return `${chalk.yellow("⚠")} reachable but answered HTTP ${res.status}`;
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
return `${chalk.red("✗")} unreachable: ${e.message}`;
|
|
65
202
|
}
|
|
66
|
-
hint("Run 'aisa mcp setup' to configure");
|
|
67
203
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.3.0";
|
|
2
2
|
/** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
|
|
3
3
|
export declare const BASE_URL = "https://api.aisa.one";
|
|
4
4
|
export declare const ENV_VAR_NAME = "AISA_API_KEY";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The MCP discovery manifest — the single source of truth for which servers
|
|
7
|
+
* exist and where they live. The CLI deliberately knows no server list of its
|
|
8
|
+
* own: servers ship and retire on the platform side, and reading the manifest
|
|
9
|
+
* at setup time means a new server works without a CLI release (the same
|
|
10
|
+
* philosophy as LLM_ROUTE_PREFIXES below, applied to the fast-moving side).
|
|
11
|
+
*
|
|
12
|
+
* History: until v0.2.4 this file hardcoded MCP_URL = "https://docs.aisa.one/mcp",
|
|
13
|
+
* a host that does not resolve — every `aisa mcp setup` wrote a dead entry into
|
|
14
|
+
* users' configs, and nothing noticed because the command had no tests.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MCP_MANIFEST_URL = "https://aisa.one/.well-known/mcp.json";
|
|
17
|
+
/** Mintlify's docs-search MCP (3 tools: search/read/feedback). Unauthenticated. */
|
|
18
|
+
export declare const DOCS_MCP_URL = "https://aisa.one/docs/mcp";
|
|
19
|
+
/**
|
|
20
|
+
* Servers configured by default. Eleven entries per client is a lot of config;
|
|
21
|
+
* these five cover the broadest agent needs, and `--all` configures everything
|
|
22
|
+
* the manifest lists as live.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MCP_DEFAULT_SLUGS: readonly string[];
|
|
6
25
|
/**
|
|
7
26
|
* First path segment of every route the LLM gateway serves under /v1 (plus
|
|
8
27
|
* /v1beta for Gemini). Everything else is an integration API under /apis/v1.
|
|
@@ -15,9 +34,17 @@ export declare const MCP_URL = "https://docs.aisa.one/mcp";
|
|
|
15
34
|
*/
|
|
16
35
|
export declare const LLM_ROUTE_PREFIXES: readonly string[];
|
|
17
36
|
export declare const AGENT_DIRS: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Per-client config shape. This is the part v0.2.4 got wrong even apart from
|
|
39
|
+
* the dead URL: it wrote `{url}` everywhere, but Claude Desktop's mcpServers
|
|
40
|
+
* only executes command/args (stdio) entries and silently ignores url ones.
|
|
41
|
+
* Stdio clients get an `npx mcp-remote` bridge instead — verified end to end
|
|
42
|
+
* against mcp.aisa.one (initialize -> tools/list -> tools/call) on 2026-08-18.
|
|
43
|
+
*/
|
|
18
44
|
export declare const MCP_CONFIGS: Record<string, {
|
|
19
45
|
path: string;
|
|
20
46
|
key: string;
|
|
47
|
+
shape: "url" | "stdio";
|
|
21
48
|
}>;
|
|
22
49
|
/**
|
|
23
50
|
* Ticker symbol → CoinGecko coin id.
|
package/dist/constants.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.3.0";
|
|
2
2
|
/** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
|
|
3
3
|
export const BASE_URL = "https://api.aisa.one";
|
|
4
4
|
export const ENV_VAR_NAME = "AISA_API_KEY";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The MCP discovery manifest — the single source of truth for which servers
|
|
7
|
+
* exist and where they live. The CLI deliberately knows no server list of its
|
|
8
|
+
* own: servers ship and retire on the platform side, and reading the manifest
|
|
9
|
+
* at setup time means a new server works without a CLI release (the same
|
|
10
|
+
* philosophy as LLM_ROUTE_PREFIXES below, applied to the fast-moving side).
|
|
11
|
+
*
|
|
12
|
+
* History: until v0.2.4 this file hardcoded MCP_URL = "https://docs.aisa.one/mcp",
|
|
13
|
+
* a host that does not resolve — every `aisa mcp setup` wrote a dead entry into
|
|
14
|
+
* users' configs, and nothing noticed because the command had no tests.
|
|
15
|
+
*/
|
|
16
|
+
export const MCP_MANIFEST_URL = "https://aisa.one/.well-known/mcp.json";
|
|
17
|
+
/** Mintlify's docs-search MCP (3 tools: search/read/feedback). Unauthenticated. */
|
|
18
|
+
export const DOCS_MCP_URL = "https://aisa.one/docs/mcp";
|
|
19
|
+
/**
|
|
20
|
+
* Servers configured by default. Eleven entries per client is a lot of config;
|
|
21
|
+
* these five cover the broadest agent needs, and `--all` configures everything
|
|
22
|
+
* the manifest lists as live.
|
|
23
|
+
*/
|
|
24
|
+
export const MCP_DEFAULT_SLUGS = [
|
|
25
|
+
"web-search",
|
|
26
|
+
"twitter-api",
|
|
27
|
+
"crypto-market-data",
|
|
28
|
+
"marketpulse",
|
|
29
|
+
"stock-pulse",
|
|
30
|
+
];
|
|
6
31
|
/**
|
|
7
32
|
* First path segment of every route the LLM gateway serves under /v1 (plus
|
|
8
33
|
* /v1beta for Gemini). Everything else is an integration API under /apis/v1.
|
|
@@ -35,15 +60,24 @@ export const AGENT_DIRS = {
|
|
|
35
60
|
gemini: "~/.gemini/skills/",
|
|
36
61
|
openclaw: "~/.openclaw/skills/",
|
|
37
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Per-client config shape. This is the part v0.2.4 got wrong even apart from
|
|
65
|
+
* the dead URL: it wrote `{url}` everywhere, but Claude Desktop's mcpServers
|
|
66
|
+
* only executes command/args (stdio) entries and silently ignores url ones.
|
|
67
|
+
* Stdio clients get an `npx mcp-remote` bridge instead — verified end to end
|
|
68
|
+
* against mcp.aisa.one (initialize -> tools/list -> tools/call) on 2026-08-18.
|
|
69
|
+
*/
|
|
38
70
|
export const MCP_CONFIGS = {
|
|
39
|
-
cursor: { path: "~/.cursor/mcp.json", key: "mcpServers" },
|
|
71
|
+
cursor: { path: "~/.cursor/mcp.json", key: "mcpServers", shape: "url" },
|
|
40
72
|
"claude-desktop": {
|
|
41
73
|
path: "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
42
74
|
key: "mcpServers",
|
|
75
|
+
shape: "stdio",
|
|
43
76
|
},
|
|
44
77
|
windsurf: {
|
|
45
78
|
path: "~/.codeium/windsurf/mcp_config.json",
|
|
46
79
|
key: "mcpServers",
|
|
80
|
+
shape: "stdio",
|
|
47
81
|
},
|
|
48
82
|
};
|
|
49
83
|
/**
|
package/dist/index.js
CHANGED
|
@@ -472,12 +472,13 @@ skills
|
|
|
472
472
|
const mcp = program.command("mcp").description("MCP server integration");
|
|
473
473
|
mcp
|
|
474
474
|
.command("setup")
|
|
475
|
-
.description("Configure AIsa MCP
|
|
475
|
+
.description("Configure AIsa MCP servers (from the live manifest) for AI agents")
|
|
476
476
|
.option("--agent <agent>", "Target agent: cursor, claude-desktop, windsurf, all")
|
|
477
|
+
.option("--all", "Configure every live server, not just the default set")
|
|
477
478
|
.action(mcpSetupAction);
|
|
478
479
|
mcp
|
|
479
480
|
.command("status")
|
|
480
|
-
.description("Check MCP
|
|
481
|
+
.description("Check MCP configuration and ping each configured endpoint")
|
|
481
482
|
.action(mcpStatusAction);
|
|
482
483
|
// ── Config ──
|
|
483
484
|
const configCmd = program.command("config").description("Manage CLI configuration");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aisa-one/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for the AIsa unified AI infrastructure platform - one API key for 80+ LLMs and 900+ endpoints across finance, search, social, and video APIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|