@zenalexa/unicli 0.210.0 → 0.211.2
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/AGENTS.md +21 -7
- package/README.md +680 -83
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/agents.d.ts +14 -3
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +369 -140
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/mcp.d.ts +3 -4
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +47 -62
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/schema.d.ts +12 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +72 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/search.d.ts +12 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +47 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/discovery/aliases.d.ts +31 -0
- package/dist/discovery/aliases.d.ts.map +1 -0
- package/dist/discovery/aliases.js +477 -0
- package/dist/discovery/aliases.js.map +1 -0
- package/dist/discovery/loader.d.ts.map +1 -1
- package/dist/discovery/loader.js +25 -0
- package/dist/discovery/loader.js.map +1 -1
- package/dist/discovery/search.d.ts +73 -0
- package/dist/discovery/search.d.ts.map +1 -0
- package/dist/discovery/search.js +355 -0
- package/dist/discovery/search.js.map +1 -0
- package/dist/manifest-compact.txt +15 -0
- package/dist/manifest-search.json +1 -0
- package/dist/manifest.json +433 -244
- package/dist/mcp/oauth.d.ts +33 -0
- package/dist/mcp/oauth.d.ts.map +1 -0
- package/dist/mcp/oauth.js +220 -0
- package/dist/mcp/oauth.js.map +1 -0
- package/dist/mcp/schema.d.ts +65 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/schema.js +136 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +23 -10
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +350 -182
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/sse-transport.d.ts +34 -0
- package/dist/mcp/sse-transport.d.ts.map +1 -0
- package/dist/mcp/sse-transport.js +182 -0
- package/dist/mcp/sse-transport.js.map +1 -0
- package/dist/mcp/streamable-http.d.ts +64 -0
- package/dist/mcp/streamable-http.d.ts.map +1 -0
- package/dist/mcp/streamable-http.js +312 -0
- package/dist/mcp/streamable-http.js.map +1 -0
- package/dist/permissions/sensitive-paths.js +2 -2
- package/dist/permissions/sensitive-paths.js.map +1 -1
- package/package.json +7 -7
- package/src/adapters/1688/_site.json +9 -0
- package/src/adapters/barchart/_site.json +10 -0
- package/src/adapters/jd/_site.json +9 -0
- package/src/adapters/linkedin/_site.json +10 -0
- package/src/adapters/macos/finder-copy.yaml +40 -0
- package/src/adapters/macos/finder-move.yaml +40 -0
- package/src/adapters/macos/finder-new-folder.yaml +36 -0
- package/src/adapters/macos/safari-history.yaml +23 -0
- package/src/adapters/macos/safari-url.yaml +22 -0
- package/src/adapters/macos/screen-recording.yaml +32 -0
- package/src/adapters/macos/wallpaper.yaml +33 -0
- package/src/adapters/reuters/_site.json +9 -0
- package/src/adapters/sinablog/_site.json +9 -0
- package/src/adapters/smzdm/_site.json +9 -0
- package/src/adapters/weixin/_site.json +9 -0
- package/src/adapters/1688/manifest.yaml +0 -7
- package/src/adapters/barchart/manifest.yaml +0 -8
- package/src/adapters/jd/manifest.yaml +0 -7
- package/src/adapters/linkedin/manifest.yaml +0 -8
- package/src/adapters/reuters/manifest.yaml +0 -7
- package/src/adapters/sinablog/manifest.yaml +0 -7
- package/src/adapters/smzdm/manifest.yaml +0 -7
- package/src/adapters/weixin/manifest.yaml +0 -7
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 Authorization Code + PKCE for MCP HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* S256-only, public clients (no client_secret), in-memory storage.
|
|
5
|
+
* Zero external dependencies — uses Node.js crypto + http built-ins.
|
|
6
|
+
*/
|
|
7
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
|
+
interface AuthCode {
|
|
9
|
+
clientId: string;
|
|
10
|
+
codeChallenge: string;
|
|
11
|
+
redirectUri: string;
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
}
|
|
14
|
+
interface Token {
|
|
15
|
+
clientId: string;
|
|
16
|
+
expiresAt: number;
|
|
17
|
+
}
|
|
18
|
+
declare function generateHex(bytes: number): string;
|
|
19
|
+
declare function sha256Base64url(input: string): string;
|
|
20
|
+
declare function pruneExpired(): void;
|
|
21
|
+
/** Returns `true` if the request was an OAuth route and has been handled. */
|
|
22
|
+
export declare function handleOAuthRoute(req: IncomingMessage, res: ServerResponse): boolean;
|
|
23
|
+
/** Returns a function that blocks unauthorized requests (returns `true` = blocked). */
|
|
24
|
+
export declare function createOAuthMiddleware(): (req: IncomingMessage, res: ServerResponse) => boolean;
|
|
25
|
+
export declare const _test: {
|
|
26
|
+
readonly authCodes: Map<string, AuthCode>;
|
|
27
|
+
readonly tokens: Map<string, Token>;
|
|
28
|
+
readonly sha256Base64url: typeof sha256Base64url;
|
|
29
|
+
readonly generateHex: typeof generateHex;
|
|
30
|
+
readonly pruneExpired: typeof pruneExpired;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=oauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/mcp/oauth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,UAAU,KAAK;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAQD,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1C;AACD,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAiCD,iBAAS,YAAY,IAAI,IAAI,CAI5B;AAwID,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAClB,OAAO,CAqBT;AAED,uFAAuF;AACvF,wBAAgB,qBAAqB,IAAI,CACvC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,KAChB,OAAO,CAmBX;AAGD,eAAO,MAAM,KAAK;;;;;;CAMR,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.1 Authorization Code + PKCE for MCP HTTP transport.
|
|
3
|
+
*
|
|
4
|
+
* S256-only, public clients (no client_secret), in-memory storage.
|
|
5
|
+
* Zero external dependencies — uses Node.js crypto + http built-ins.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
8
|
+
const authCodes = new Map();
|
|
9
|
+
const tokens = new Map();
|
|
10
|
+
const AUTH_CODE_TTL_MS = 60_000;
|
|
11
|
+
const TOKEN_TTL_S = 3_600;
|
|
12
|
+
const TOKEN_TTL_MS = TOKEN_TTL_S * 1_000;
|
|
13
|
+
function generateHex(bytes) {
|
|
14
|
+
return randomBytes(bytes).toString("hex");
|
|
15
|
+
}
|
|
16
|
+
function sha256Base64url(input) {
|
|
17
|
+
return createHash("sha256").update(input).digest("base64url");
|
|
18
|
+
}
|
|
19
|
+
function parseQuery(url) {
|
|
20
|
+
const i = url.indexOf("?");
|
|
21
|
+
return new URLSearchParams(i >= 0 ? url.slice(i + 1) : "");
|
|
22
|
+
}
|
|
23
|
+
function readBody(req) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const chunks = [];
|
|
26
|
+
let size = 0;
|
|
27
|
+
req.on("data", (c) => {
|
|
28
|
+
size += c.length;
|
|
29
|
+
if (size > 65_536) {
|
|
30
|
+
req.destroy();
|
|
31
|
+
reject(new Error("Body too large"));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
chunks.push(c);
|
|
35
|
+
});
|
|
36
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
37
|
+
req.on("error", reject);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function json(res, status, body) {
|
|
41
|
+
res.writeHead(status, {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
"Cache-Control": "no-store",
|
|
44
|
+
});
|
|
45
|
+
res.end(JSON.stringify(body));
|
|
46
|
+
}
|
|
47
|
+
function pruneExpired() {
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
for (const [k, v] of authCodes)
|
|
50
|
+
if (v.expiresAt <= now)
|
|
51
|
+
authCodes.delete(k);
|
|
52
|
+
for (const [k, v] of tokens)
|
|
53
|
+
if (v.expiresAt <= now)
|
|
54
|
+
tokens.delete(k);
|
|
55
|
+
}
|
|
56
|
+
// ── Authorization Endpoint ─────────────────────────────────────────────────
|
|
57
|
+
const HTML = (cid, ch, ru) => `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>Uni-CLI MCP Auth</title>` +
|
|
58
|
+
`<style>body{font-family:system-ui,sans-serif;max-width:420px;margin:80px auto;text-align:center}` +
|
|
59
|
+
`button{padding:12px 32px;font-size:16px;cursor:pointer;border:none;border-radius:6px;` +
|
|
60
|
+
`background:#2563eb;color:#fff}code{background:#f1f5f9;padding:2px 6px;border-radius:3px}</style>` +
|
|
61
|
+
`</head><body><h2>Authorize MCP Client</h2><p>Client <code>${cid}</code> requests access.</p>` +
|
|
62
|
+
`<form method="POST" action="/oauth/authorize"><input type="hidden" name="client_id" value="${cid}">` +
|
|
63
|
+
`<input type="hidden" name="code_challenge" value="${ch}"><input type="hidden" name="redirect_uri" ` +
|
|
64
|
+
`value="${ru}"><button type="submit">Grant Access</button></form></body></html>`;
|
|
65
|
+
function handleAuthorizeGet(req, res) {
|
|
66
|
+
const p = parseQuery(req.url ?? "");
|
|
67
|
+
const clientId = p.get("client_id"), challenge = p.get("code_challenge");
|
|
68
|
+
const method = p.get("code_challenge_method"), redirect = p.get("redirect_uri");
|
|
69
|
+
if (!clientId || !challenge || !redirect)
|
|
70
|
+
return json(res, 400, {
|
|
71
|
+
error: "invalid_request",
|
|
72
|
+
error_description: "Missing client_id, code_challenge, or redirect_uri",
|
|
73
|
+
});
|
|
74
|
+
if (method && method !== "S256")
|
|
75
|
+
return json(res, 400, {
|
|
76
|
+
error: "invalid_request",
|
|
77
|
+
error_description: "Only S256 code_challenge_method is supported",
|
|
78
|
+
});
|
|
79
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
80
|
+
res.end(HTML(clientId, challenge, redirect));
|
|
81
|
+
}
|
|
82
|
+
async function handleAuthorizePost(req, res) {
|
|
83
|
+
const p = new URLSearchParams(await readBody(req));
|
|
84
|
+
const clientId = p.get("client_id"), challenge = p.get("code_challenge"), redirect = p.get("redirect_uri");
|
|
85
|
+
if (!clientId || !challenge || !redirect)
|
|
86
|
+
return json(res, 400, {
|
|
87
|
+
error: "invalid_request",
|
|
88
|
+
error_description: "Missing required parameters",
|
|
89
|
+
});
|
|
90
|
+
pruneExpired();
|
|
91
|
+
const code = generateHex(32);
|
|
92
|
+
authCodes.set(code, {
|
|
93
|
+
clientId,
|
|
94
|
+
codeChallenge: challenge,
|
|
95
|
+
redirectUri: redirect,
|
|
96
|
+
expiresAt: Date.now() + AUTH_CODE_TTL_MS,
|
|
97
|
+
});
|
|
98
|
+
res.writeHead(302, {
|
|
99
|
+
Location: `${redirect}${redirect.includes("?") ? "&" : "?"}code=${code}`,
|
|
100
|
+
});
|
|
101
|
+
res.end();
|
|
102
|
+
}
|
|
103
|
+
// ── Token Endpoint ─────────────────────────────────────────────────────────
|
|
104
|
+
async function handleToken(req, res) {
|
|
105
|
+
const p = new URLSearchParams(await readBody(req));
|
|
106
|
+
const grant = p.get("grant_type"), code = p.get("code");
|
|
107
|
+
const verifier = p.get("code_verifier"), clientId = p.get("client_id");
|
|
108
|
+
if (grant !== "authorization_code")
|
|
109
|
+
return json(res, 400, {
|
|
110
|
+
error: "unsupported_grant_type",
|
|
111
|
+
error_description: "Only authorization_code is supported",
|
|
112
|
+
});
|
|
113
|
+
if (!code || !verifier || !clientId)
|
|
114
|
+
return json(res, 400, {
|
|
115
|
+
error: "invalid_request",
|
|
116
|
+
error_description: "Missing code, code_verifier, or client_id",
|
|
117
|
+
});
|
|
118
|
+
pruneExpired();
|
|
119
|
+
const entry = authCodes.get(code);
|
|
120
|
+
if (!entry)
|
|
121
|
+
return json(res, 400, {
|
|
122
|
+
error: "invalid_grant",
|
|
123
|
+
error_description: "Invalid or expired authorization code",
|
|
124
|
+
});
|
|
125
|
+
authCodes.delete(code); // single-use
|
|
126
|
+
if (entry.expiresAt <= Date.now())
|
|
127
|
+
return json(res, 400, {
|
|
128
|
+
error: "invalid_grant",
|
|
129
|
+
error_description: "Authorization code has expired",
|
|
130
|
+
});
|
|
131
|
+
if (entry.clientId !== clientId)
|
|
132
|
+
return json(res, 400, {
|
|
133
|
+
error: "invalid_grant",
|
|
134
|
+
error_description: "Client ID mismatch",
|
|
135
|
+
});
|
|
136
|
+
if (sha256Base64url(verifier) !== entry.codeChallenge)
|
|
137
|
+
return json(res, 400, {
|
|
138
|
+
error: "invalid_grant",
|
|
139
|
+
error_description: "PKCE verification failed",
|
|
140
|
+
});
|
|
141
|
+
const accessToken = generateHex(32);
|
|
142
|
+
tokens.set(accessToken, { clientId, expiresAt: Date.now() + TOKEN_TTL_MS });
|
|
143
|
+
json(res, 200, {
|
|
144
|
+
access_token: accessToken,
|
|
145
|
+
token_type: "Bearer",
|
|
146
|
+
expires_in: TOKEN_TTL_S,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// ── Bearer Validation ──────────────────────────────────────────────────────
|
|
150
|
+
function validateBearer(req) {
|
|
151
|
+
const h = req.headers.authorization;
|
|
152
|
+
if (!h)
|
|
153
|
+
return false;
|
|
154
|
+
const parts = h.split(" ");
|
|
155
|
+
if (parts.length !== 2 || parts[0] !== "Bearer")
|
|
156
|
+
return false;
|
|
157
|
+
const entry = tokens.get(parts[1]);
|
|
158
|
+
if (!entry)
|
|
159
|
+
return false;
|
|
160
|
+
if (entry.expiresAt <= Date.now()) {
|
|
161
|
+
tokens.delete(parts[1]);
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
167
|
+
/** Returns `true` if the request was an OAuth route and has been handled. */
|
|
168
|
+
export function handleOAuthRoute(req, res) {
|
|
169
|
+
const path = (req.url ?? "").split("?")[0];
|
|
170
|
+
if (path === "/oauth/authorize") {
|
|
171
|
+
if (req.method === "GET") {
|
|
172
|
+
handleAuthorizeGet(req, res);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (req.method === "POST") {
|
|
176
|
+
handleAuthorizePost(req, res).catch(() => {
|
|
177
|
+
if (!res.writableEnded)
|
|
178
|
+
json(res, 500, { error: "server_error" });
|
|
179
|
+
});
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (path === "/oauth/token" && req.method === "POST") {
|
|
184
|
+
handleToken(req, res).catch(() => {
|
|
185
|
+
if (!res.writableEnded)
|
|
186
|
+
json(res, 500, { error: "server_error" });
|
|
187
|
+
});
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
/** Returns a function that blocks unauthorized requests (returns `true` = blocked). */
|
|
193
|
+
export function createOAuthMiddleware() {
|
|
194
|
+
return (req, res) => {
|
|
195
|
+
if (validateBearer(req))
|
|
196
|
+
return false;
|
|
197
|
+
res.writeHead(401, {
|
|
198
|
+
"Content-Type": "application/json",
|
|
199
|
+
"WWW-Authenticate": 'Bearer realm="unicli-mcp"',
|
|
200
|
+
});
|
|
201
|
+
res.end(JSON.stringify({
|
|
202
|
+
jsonrpc: "2.0",
|
|
203
|
+
id: null,
|
|
204
|
+
error: {
|
|
205
|
+
code: -32600,
|
|
206
|
+
message: "Unauthorized: valid Bearer token required",
|
|
207
|
+
},
|
|
208
|
+
}));
|
|
209
|
+
return true;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
// Test helpers — exported for unit tests only
|
|
213
|
+
export const _test = {
|
|
214
|
+
authCodes,
|
|
215
|
+
tokens,
|
|
216
|
+
sha256Base64url,
|
|
217
|
+
generateHex,
|
|
218
|
+
pruneExpired,
|
|
219
|
+
};
|
|
220
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/mcp/oauth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AActD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;AAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;AACxC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,WAAW,GAAG,KAAK,CAAC;AAC1B,MAAM,YAAY,GAAG,WAAW,GAAG,KAAK,CAAC;AAEzC,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC5C,CAAC;AACD,SAAS,eAAe,CAAC,KAAa;IACpC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAChE,CAAC;AACD,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,IAAI,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC7D,CAAC;AACD,SAAS,QAAQ,CAAC,GAAoB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YAC3B,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;YACjB,IAAI,IAAI,GAAG,MAAM,EAAE,CAAC;gBAClB,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;gBACpC,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AACD,SAAS,IAAI,CACX,GAAmB,EACnB,MAAc,EACd,IAA6B;IAE7B,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE;QACpB,cAAc,EAAE,kBAAkB;QAClC,eAAe,EAAE,UAAU;KAC5B,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AACD,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS;QAAE,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG;YAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5E,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM;QAAE,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG;YAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,8EAA8E;AAE9E,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,EAAU,EAAE,EAAU,EAAE,EAAE,CACnD,4FAA4F;IAC5F,kGAAkG;IAClG,uFAAuF;IACvF,kGAAkG;IAClG,6DAA6D,GAAG,8BAA8B;IAC9F,8FAA8F,GAAG,IAAI;IACrG,qDAAqD,EAAE,6CAA6C;IACpG,UAAU,EAAE,oEAAoE,CAAC;AAEnF,SAAS,kBAAkB,CAAC,GAAoB,EAAE,GAAmB;IACnE,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,EACjC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,uBAAuB,CAAC,EAC3C,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;QACtC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,iBAAiB;YACxB,iBAAiB,EAAE,oDAAoD;SACxE,CAAC,CAAC;IACL,IAAI,MAAM,IAAI,MAAM,KAAK,MAAM;QAC7B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,iBAAiB;YACxB,iBAAiB,EAAE,8CAA8C;SAClE,CAAC,CAAC;IACL,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;IACnE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,GAAoB,EACpB,GAAmB;IAEnB,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,EACjC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,EACnC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACnC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ;QACtC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,iBAAiB;YACxB,iBAAiB,EAAE,6BAA6B;SACjD,CAAC,CAAC;IACL,YAAY,EAAE,CAAC;IACf,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE;QAClB,QAAQ;QACR,aAAa,EAAE,SAAS;QACxB,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB;KACzC,CAAC,CAAC;IACH,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;QACjB,QAAQ,EAAE,GAAG,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,EAAE;KACzE,CAAC,CAAC;IACH,GAAG,CAAC,GAAG,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAE9E,KAAK,UAAU,WAAW,CACxB,GAAoB,EACpB,GAAmB;IAEnB,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAC/B,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,EACrC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEhC,IAAI,KAAK,KAAK,oBAAoB;QAChC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,wBAAwB;YAC/B,iBAAiB,EAAE,sCAAsC;SAC1D,CAAC,CAAC;IACL,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ;QACjC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,iBAAiB;YACxB,iBAAiB,EAAE,2CAA2C;SAC/D,CAAC,CAAC;IAEL,YAAY,EAAE,CAAC;IACf,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QACR,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,eAAe;YACtB,iBAAiB,EAAE,uCAAuC;SAC3D,CAAC,CAAC;IACL,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa;IAErC,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE;QAC/B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,eAAe;YACtB,iBAAiB,EAAE,gCAAgC;SACpD,CAAC,CAAC;IACL,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ;QAC7B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,eAAe;YACtB,iBAAiB,EAAE,oBAAoB;SACxC,CAAC,CAAC;IACL,IAAI,eAAe,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,aAAa;QACnD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;YACpB,KAAK,EAAE,eAAe;YACtB,iBAAiB,EAAE,0BAA0B;SAC9C,CAAC,CAAC;IAEL,MAAM,WAAW,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;QACb,YAAY,EAAE,WAAW;QACzB,UAAU,EAAE,QAAQ;QACpB,UAAU,EAAE,WAAW;KACxB,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAE9E,SAAS,cAAc,CAAC,GAAoB;IAC1C,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAE9E,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAC9B,GAAoB,EACpB,GAAmB;IAEnB,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACzB,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC1B,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBACvC,IAAI,CAAC,GAAG,CAAC,aAAa;oBAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YACpE,CAAC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QACrD,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,GAAG,CAAC,aAAa;gBAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,qBAAqB;IAInC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAClB,IAAI,cAAc,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,cAAc,EAAE,kBAAkB;YAClC,kBAAkB,EAAE,2BAA2B;SAChD,CAAC,CAAC;QACH,GAAG,CAAC,GAAG,CACL,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE,KAAK;YACd,EAAE,EAAE,IAAI;YACR,KAAK,EAAE;gBACL,IAAI,EAAE,CAAC,KAAK;gBACZ,OAAO,EAAE,2CAA2C;aACrD;SACF,CAAC,CACH,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAED,8CAA8C;AAC9C,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,SAAS;IACT,MAAM;IACN,eAAe;IACf,WAAW;IACX,YAAY;CACJ,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON Schema builders for MCP tools and CLI schema command.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for:
|
|
5
|
+
* - Adapter arg → JSON Schema type mapping
|
|
6
|
+
* - Input/output schema generation from AdapterCommand metadata
|
|
7
|
+
* - Tool name normalization (site + command → MCP tool name)
|
|
8
|
+
* - Description truncation within token budgets
|
|
9
|
+
*/
|
|
10
|
+
import type { AdapterCommand } from "../types.js";
|
|
11
|
+
export interface JsonSchemaProperty {
|
|
12
|
+
type: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
default?: unknown;
|
|
15
|
+
enum?: string[];
|
|
16
|
+
additionalProperties?: boolean;
|
|
17
|
+
items?: JsonSchemaProperty;
|
|
18
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
19
|
+
}
|
|
20
|
+
export interface JsonSchemaObject {
|
|
21
|
+
type: "object" | "array";
|
|
22
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
23
|
+
required?: string[];
|
|
24
|
+
additionalProperties?: boolean;
|
|
25
|
+
items?: JsonSchemaProperty;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Map adapter `arg.type` to JSON Schema primitive.
|
|
29
|
+
* Defaults to "string" for unknown/missing — safer than failing the build.
|
|
30
|
+
*/
|
|
31
|
+
export declare function jsonTypeFor(t: string | undefined): string;
|
|
32
|
+
/**
|
|
33
|
+
* Build JSON Schema for a command's input from its `args` definition.
|
|
34
|
+
* Always includes a `limit` parameter (default 20) for result capping.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildInputSchema(cmd: AdapterCommand): JsonSchemaObject;
|
|
37
|
+
/**
|
|
38
|
+
* Build JSON Schema for a command's output.
|
|
39
|
+
*
|
|
40
|
+
* Two formats:
|
|
41
|
+
* - `wrapped` (MCP): `{count: int, results: [{...columns}]}`
|
|
42
|
+
* - `flat` (CLI schema): `[{...columns}]`
|
|
43
|
+
*
|
|
44
|
+
* MCP clients expect the wrapped format; the CLI `unicli schema` command
|
|
45
|
+
* exposes the flat array format for simpler consumption.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildOutputSchema(cmd: AdapterCommand, format?: "wrapped" | "flat"): JsonSchemaObject;
|
|
48
|
+
/**
|
|
49
|
+
* Build normalized MCP tool name: `unicli_<site>_<command>`.
|
|
50
|
+
*
|
|
51
|
+
* Non-alphanumeric chars collapse to `_`. This normalization is NOT
|
|
52
|
+
* reversible — callers must use a lookup table for reverse mapping.
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildToolName(site: string, command: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Approximate token count. Uses `words × 1.3` — closely tracks
|
|
57
|
+
* tiktoken cl100k for English + mixed-case identifiers.
|
|
58
|
+
*/
|
|
59
|
+
export declare function approxTokens(text: string): number;
|
|
60
|
+
/**
|
|
61
|
+
* Truncate description to fit within a token budget.
|
|
62
|
+
* Cuts at word boundary, appends "…" when truncated.
|
|
63
|
+
*/
|
|
64
|
+
export declare function truncateDescription(desc: string, maxTokens?: number): string;
|
|
65
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/mcp/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAID;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYzD;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,gBAAgB,CA6BtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,cAAc,EACnB,MAAM,GAAE,SAAS,GAAG,MAAkB,GACrC,gBAAgB,CA0BlB;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnE;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAUxE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON Schema builders for MCP tools and CLI schema command.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for:
|
|
5
|
+
* - Adapter arg → JSON Schema type mapping
|
|
6
|
+
* - Input/output schema generation from AdapterCommand metadata
|
|
7
|
+
* - Tool name normalization (site + command → MCP tool name)
|
|
8
|
+
* - Description truncation within token budgets
|
|
9
|
+
*/
|
|
10
|
+
// ── Type Mapping ────────────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Map adapter `arg.type` to JSON Schema primitive.
|
|
13
|
+
* Defaults to "string" for unknown/missing — safer than failing the build.
|
|
14
|
+
*/
|
|
15
|
+
export function jsonTypeFor(t) {
|
|
16
|
+
switch (t) {
|
|
17
|
+
case "int":
|
|
18
|
+
return "integer";
|
|
19
|
+
case "float":
|
|
20
|
+
return "number";
|
|
21
|
+
case "bool":
|
|
22
|
+
return "boolean";
|
|
23
|
+
case "str":
|
|
24
|
+
default:
|
|
25
|
+
return "string";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// ── Schema Builders ─────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Build JSON Schema for a command's input from its `args` definition.
|
|
31
|
+
* Always includes a `limit` parameter (default 20) for result capping.
|
|
32
|
+
*/
|
|
33
|
+
export function buildInputSchema(cmd) {
|
|
34
|
+
const props = {
|
|
35
|
+
limit: {
|
|
36
|
+
type: "integer",
|
|
37
|
+
description: "Cap result count (default 20)",
|
|
38
|
+
default: 20,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
const required = [];
|
|
42
|
+
for (const a of cmd.adapterArgs ?? []) {
|
|
43
|
+
if (a.name === "limit")
|
|
44
|
+
continue;
|
|
45
|
+
const prop = {
|
|
46
|
+
type: jsonTypeFor(a.type),
|
|
47
|
+
description: a.description,
|
|
48
|
+
};
|
|
49
|
+
if (a.default !== undefined)
|
|
50
|
+
prop.default = a.default;
|
|
51
|
+
if (a.choices)
|
|
52
|
+
prop.enum = a.choices;
|
|
53
|
+
props[a.name] = prop;
|
|
54
|
+
if (a.required)
|
|
55
|
+
required.push(a.name);
|
|
56
|
+
}
|
|
57
|
+
const schema = {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: props,
|
|
60
|
+
additionalProperties: false,
|
|
61
|
+
};
|
|
62
|
+
if (required.length > 0)
|
|
63
|
+
schema.required = required;
|
|
64
|
+
return schema;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build JSON Schema for a command's output.
|
|
68
|
+
*
|
|
69
|
+
* Two formats:
|
|
70
|
+
* - `wrapped` (MCP): `{count: int, results: [{...columns}]}`
|
|
71
|
+
* - `flat` (CLI schema): `[{...columns}]`
|
|
72
|
+
*
|
|
73
|
+
* MCP clients expect the wrapped format; the CLI `unicli schema` command
|
|
74
|
+
* exposes the flat array format for simpler consumption.
|
|
75
|
+
*/
|
|
76
|
+
export function buildOutputSchema(cmd, format = "wrapped") {
|
|
77
|
+
const itemProps = {};
|
|
78
|
+
for (const col of cmd.columns ?? []) {
|
|
79
|
+
itemProps[col] = { type: "string", description: `Column: ${col}` };
|
|
80
|
+
}
|
|
81
|
+
const itemSchema = {
|
|
82
|
+
type: "object",
|
|
83
|
+
...(Object.keys(itemProps).length > 0 ? { properties: itemProps } : {}),
|
|
84
|
+
};
|
|
85
|
+
if (format === "flat") {
|
|
86
|
+
return { type: "array", items: itemSchema };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
count: { type: "integer", description: "Number of results returned" },
|
|
92
|
+
results: {
|
|
93
|
+
type: "array",
|
|
94
|
+
description: "Result rows",
|
|
95
|
+
items: itemSchema,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// ── Tool Name ───────────────────────────────────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* Build normalized MCP tool name: `unicli_<site>_<command>`.
|
|
103
|
+
*
|
|
104
|
+
* Non-alphanumeric chars collapse to `_`. This normalization is NOT
|
|
105
|
+
* reversible — callers must use a lookup table for reverse mapping.
|
|
106
|
+
*/
|
|
107
|
+
export function buildToolName(site, command) {
|
|
108
|
+
return `unicli_${site}_${command}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
109
|
+
}
|
|
110
|
+
// ── Description Truncation ──────────────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* Approximate token count. Uses `words × 1.3` — closely tracks
|
|
113
|
+
* tiktoken cl100k for English + mixed-case identifiers.
|
|
114
|
+
*/
|
|
115
|
+
export function approxTokens(text) {
|
|
116
|
+
const words = text.split(/\s+/).filter(Boolean).length;
|
|
117
|
+
return Math.ceil(words * 1.3);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Truncate description to fit within a token budget.
|
|
121
|
+
* Cuts at word boundary, appends "…" when truncated.
|
|
122
|
+
*/
|
|
123
|
+
export function truncateDescription(desc, maxTokens = 68) {
|
|
124
|
+
if (approxTokens(desc) <= maxTokens)
|
|
125
|
+
return desc;
|
|
126
|
+
const words = desc.split(/\s+/).filter(Boolean);
|
|
127
|
+
let result = "";
|
|
128
|
+
for (const word of words) {
|
|
129
|
+
const candidate = result ? `${result} ${word}` : word;
|
|
130
|
+
if (approxTokens(candidate + " …") > maxTokens)
|
|
131
|
+
break;
|
|
132
|
+
result = candidate;
|
|
133
|
+
}
|
|
134
|
+
return result ? `${result} …` : words[0] + " …";
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/mcp/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -3,24 +3,37 @@
|
|
|
3
3
|
* MCP (Model Context Protocol) server for Uni-CLI.
|
|
4
4
|
*
|
|
5
5
|
* Two registration modes:
|
|
6
|
-
* 1. **
|
|
6
|
+
* 1. **Smart default** — 3 tools: `unicli_run`, `unicli_list`,
|
|
7
|
+
* `unicli_discover`. Keeps the MCP handshake under 200 tokens.
|
|
8
|
+
* 2. **Expanded (`--expanded`)** — one tool per adapter command
|
|
7
9
|
* (`unicli_<site>_<command>`) with JSON Schema derived from `args` +
|
|
8
|
-
* `columns`.
|
|
9
|
-
* MCP clients see the full Uni-CLI surface area without an extra
|
|
10
|
-
* list_adapters → run_command roundtrip.
|
|
11
|
-
* 2. **Lazy (`--lazy`)** — only `list_adapters` + `run_command` are
|
|
12
|
-
* registered. Useful when an MCP client has a hard tool-count limit
|
|
13
|
-
* or wants the smallest possible handshake.
|
|
10
|
+
* `columns`. MCP clients see the full Uni-CLI surface area.
|
|
14
11
|
*
|
|
15
|
-
*
|
|
12
|
+
* Three transports:
|
|
16
13
|
* - **stdio (default)** — newline-delimited JSON over stdin/stdout
|
|
17
14
|
* - **http (`--transport http [--port 19826]`)** — POST /mcp accepts a
|
|
18
|
-
* single JSON-RPC envelope and returns a single JSON response.
|
|
19
|
-
*
|
|
15
|
+
* single JSON-RPC envelope and returns a single JSON response.
|
|
16
|
+
* - **sse (`--transport sse [--port 19826]`)** — Streamable HTTP with
|
|
17
|
+
* Server-Sent Events. GET /mcp/sse opens the event stream, POST
|
|
18
|
+
* /mcp/message?sessionId=xxx delivers JSON-RPC requests.
|
|
20
19
|
*
|
|
21
20
|
* Auth pass-through is automatic: every adapter the CLI loads (including
|
|
22
21
|
* cookie-based ones) is exposed by name; the runtime resolves cookies on
|
|
23
22
|
* each call via the same code path as the CLI.
|
|
24
23
|
*/
|
|
24
|
+
interface McpToolResult {
|
|
25
|
+
content: Array<{
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}>;
|
|
29
|
+
isError?: boolean;
|
|
30
|
+
_meta?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Annotate a tool result with `_meta.anthropic/maxResultSizeChars` when the
|
|
34
|
+
* serialized payload exceeds MAX_RESULT_SIZE_CHARS (10 KB). This tells
|
|
35
|
+
* Claude Code to accept large payloads without truncation.
|
|
36
|
+
*/
|
|
37
|
+
export declare function annotateIfLarge(result: McpToolResult): McpToolResult;
|
|
25
38
|
export {};
|
|
26
39
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA2DH,UAAU,aAAa;IACrB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAmYD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CASpE"}
|