pikiloom 0.4.26 → 0.4.27
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/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DIP-y4dl.js} +1 -1
- package/dashboard/dist/assets/ConnectionModal-BCfvhkiI.js +1 -0
- package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-Dot7ua9-.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-GioMx4S6.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C9_r8jJP.js} +1 -1
- package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-M1AP1sgR.js} +1 -1
- package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-CCm3UUfJ.js} +1 -1
- package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-YubDM16P.js} +1 -1
- package/dashboard/dist/assets/{SessionPanel-_0dLWvEb.js → SessionPanel-Cnv9o96n.js} +1 -1
- package/dashboard/dist/assets/SystemTab-VgNivIe3.js +1 -0
- package/dashboard/dist/assets/index-BY7l20bi.js +23 -0
- package/dashboard/dist/assets/index-BlqvzDUL.js +3 -0
- package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
- package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C0SAInnT.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/acp-client.js +0 -59
- package/dist/agent/artifacts.js +1 -38
- package/dist/agent/auto-update.js +2 -78
- package/dist/agent/await-resume.js +0 -20
- package/dist/agent/cli/auth.js +3 -51
- package/dist/agent/cli/catalog.js +0 -3
- package/dist/agent/cli/detector.js +1 -19
- package/dist/agent/cli/index.js +0 -3
- package/dist/agent/cli/registry.js +0 -25
- package/dist/agent/driver.js +0 -14
- package/dist/agent/drivers/claude-tui.js +2 -769
- package/dist/agent/drivers/claude.js +12 -631
- package/dist/agent/drivers/codex.js +11 -162
- package/dist/agent/drivers/gemini.js +6 -114
- package/dist/agent/drivers/hermes.js +0 -128
- package/dist/agent/goal.js +0 -45
- package/dist/agent/handover.js +1 -46
- package/dist/agent/images.js +7 -133
- package/dist/agent/index.js +1 -29
- package/dist/agent/mcp/bridge.js +1 -197
- package/dist/agent/mcp/extensions.js +5 -84
- package/dist/agent/mcp/oauth.js +2 -69
- package/dist/agent/mcp/registry.js +2 -33
- package/dist/agent/mcp/session-server.js +2 -45
- package/dist/agent/mcp/tools/ask-user.js +0 -6
- package/dist/agent/mcp/tools/await-resume.js +0 -15
- package/dist/agent/mcp/tools/goal.js +0 -25
- package/dist/agent/mcp/tools/types.js +0 -5
- package/dist/agent/mcp/tools/workspace.js +0 -23
- package/dist/agent/npm.js +0 -14
- package/dist/agent/session.js +86 -196
- package/dist/agent/skill-installer.js +3 -70
- package/dist/agent/skills.js +0 -43
- package/dist/agent/stream.js +5 -148
- package/dist/agent/types.js +0 -12
- package/dist/agent/utils.js +2 -120
- package/dist/bot/bot.js +3 -468
- package/dist/bot/command-ui.js +0 -45
- package/dist/bot/commands.js +0 -51
- package/dist/bot/headless-bot.js +0 -21
- package/dist/bot/host.js +1 -5
- package/dist/bot/human-loop.js +0 -8
- package/dist/bot/menu.js +0 -3
- package/dist/bot/orchestration.js +0 -3
- package/dist/bot/render-shared.js +0 -63
- package/dist/bot/session-hub.js +14 -92
- package/dist/bot/session-status.js +1 -5
- package/dist/bot/streaming.js +0 -53
- package/dist/browser-profile.js +0 -51
- package/dist/browser-supervisor.js +0 -75
- package/dist/catalog/cli-tools.js +0 -53
- package/dist/catalog/index.js +0 -18
- package/dist/catalog/local-models.js +0 -18
- package/dist/catalog/mcp-servers.js +0 -35
- package/dist/catalog/skill-repos.js +0 -32
- package/dist/channels/base.js +0 -19
- package/dist/channels/dingtalk/bot.js +0 -3
- package/dist/channels/dingtalk/channel.js +0 -33
- package/dist/channels/discord/bot.js +0 -7
- package/dist/channels/discord/channel.js +0 -22
- package/dist/channels/feishu/bot.js +0 -71
- package/dist/channels/feishu/channel.js +0 -78
- package/dist/channels/feishu/markdown.js +0 -5
- package/dist/channels/feishu/render.js +0 -45
- package/dist/channels/health.js +0 -46
- package/dist/channels/slack/bot.js +0 -7
- package/dist/channels/slack/channel.js +1 -27
- package/dist/channels/states.js +0 -3
- package/dist/channels/telegram/bot.js +0 -60
- package/dist/channels/telegram/channel.js +2 -99
- package/dist/channels/telegram/directory.js +0 -3
- package/dist/channels/telegram/live-preview.js +0 -8
- package/dist/channels/telegram/render.js +0 -30
- package/dist/channels/wecom/bot.js +0 -3
- package/dist/channels/wecom/channel.js +0 -58
- package/dist/channels/weixin/api.js +0 -9
- package/dist/channels/weixin/bot.js +0 -88
- package/dist/channels/weixin/channel.js +0 -6
- package/dist/cli/autostart.js +1 -44
- package/dist/cli/channel-supervisor.js +1 -59
- package/dist/cli/channels.js +0 -6
- package/dist/cli/main.js +1 -145
- package/dist/cli/onboarding.js +0 -6
- package/dist/cli/run.js +0 -11
- package/dist/cli/setup-wizard.js +0 -3
- package/dist/core/config/runtime-config.js +0 -38
- package/dist/core/config/user-config.js +0 -63
- package/dist/core/config/validation.js +0 -9
- package/dist/core/constants.js +0 -268
- package/dist/core/git.js +0 -31
- package/dist/core/legacy-compat.js +1 -28
- package/dist/core/logging.js +0 -3
- package/dist/core/platform.js +0 -28
- package/dist/core/process-control.js +0 -8
- package/dist/core/secrets/index.js +0 -17
- package/dist/core/secrets/inline-seal.js +0 -7
- package/dist/core/secrets/ref.js +0 -19
- package/dist/core/secrets/resolver.js +0 -8
- package/dist/core/secrets/store.js +1 -12
- package/dist/core/utils.js +1 -37
- package/dist/core/version.js +0 -3
- package/dist/dashboard/platform.js +1 -35
- package/dist/dashboard/routes/agents.js +2 -71
- package/dist/dashboard/routes/cli.js +0 -27
- package/dist/dashboard/routes/config.js +0 -54
- package/dist/dashboard/routes/extensions.js +0 -132
- package/dist/dashboard/routes/local-models.js +0 -68
- package/dist/dashboard/routes/models.js +0 -46
- package/dist/dashboard/routes/sessions.js +0 -100
- package/dist/dashboard/runtime.js +1 -58
- package/dist/dashboard/server.js +0 -49
- package/dist/dashboard/session-control.js +2 -69
- package/dist/model/catalog.js +1 -17
- package/dist/model/index.js +0 -15
- package/dist/model/injector.js +1 -181
- package/dist/model/provider-models.js +1 -48
- package/dist/model/responses-bridge.js +3 -45
- package/dist/model/store.js +0 -17
- package/dist/model/types.js +0 -12
- package/dist/model/validation.js +1 -18
- package/dist/pikichannel/adapter-pikiloom.js +0 -31
- package/dist/pikichannel/code.js +0 -12
- package/dist/pikichannel/codec.js +0 -14
- package/dist/pikichannel/host.js +4 -33
- package/dist/pikichannel/protocol.js +0 -52
- package/dist/pikichannel/rendezvous-broker.js +5 -26
- package/dist/pikichannel/rendezvous-host.js +3 -16
- package/dist/pikichannel/server.js +6 -55
- package/dist/pikichannel/transport.js +0 -25
- package/dist/pikichannel/transports/webrtc-host.js +0 -18
- package/dist/pikichannel/transports/webrtc-shared.js +4 -34
- package/dist/pikichannel/transports/websocket-host.js +1 -14
- package/dist/pikichannel/turn.js +2 -69
- package/dist/pikichannel/web/sdk.js +14 -71
- package/package.json +1 -1
- package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
- package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
- package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
- package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
- package/dashboard/dist/assets/index-CyCspgtc.css +0 -1
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP extension management — CRUD, catalog merge, health check, session merge.
|
|
3
|
-
*
|
|
4
|
-
* Global extensions live in ~/.pikiloom/setting.json under extensions.mcp.
|
|
5
|
-
* Workspace extensions live in <workdir>/.mcp.json (standard format).
|
|
6
|
-
*
|
|
7
|
-
* getCatalogItems() produces the unified list the dashboard renders:
|
|
8
|
-
* recommended-registry entries merged with installed entries, with a single
|
|
9
|
-
* state field per item (recommended | needs_auth | disabled | ready | unhealthy).
|
|
10
|
-
*
|
|
11
|
-
* mergeExtensionsForSession() is called by bridge.ts before spawning an agent —
|
|
12
|
-
* it resolves disabled flags, expands OAuth Bearer headers from the token store,
|
|
13
|
-
* and hands the final config map to the agent CLI.
|
|
14
|
-
*/
|
|
15
1
|
import fs from 'node:fs';
|
|
16
2
|
import os from 'node:os';
|
|
17
3
|
import path from 'node:path';
|
|
@@ -20,9 +6,6 @@ import { loadUserConfig, saveUserConfig } from '../../core/config/user-config.js
|
|
|
20
6
|
import { terminateProcessTree } from '../../core/process-control.js';
|
|
21
7
|
import { getRecommendedMcpServers, } from './registry.js';
|
|
22
8
|
import { hasValidMcpToken, injectOAuthHeaders } from './oauth.js';
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
// Global extensions (setting.json)
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
9
|
export function loadGlobalMcpExtensions() {
|
|
27
10
|
const config = loadUserConfig();
|
|
28
11
|
const mcp = config.extensions?.mcp;
|
|
@@ -65,9 +48,6 @@ export function updateGlobalMcpExtension(name, patch) {
|
|
|
65
48
|
});
|
|
66
49
|
return true;
|
|
67
50
|
}
|
|
68
|
-
// ---------------------------------------------------------------------------
|
|
69
|
-
// Workspace extensions (.mcp.json)
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
51
|
function workspaceMcpJsonPath(workdir) {
|
|
72
52
|
return path.join(workdir, '.mcp.json');
|
|
73
53
|
}
|
|
@@ -80,7 +60,7 @@ function readMcpJson(filePath) {
|
|
|
80
60
|
return servers;
|
|
81
61
|
}
|
|
82
62
|
}
|
|
83
|
-
catch {
|
|
63
|
+
catch { }
|
|
84
64
|
return {};
|
|
85
65
|
}
|
|
86
66
|
function writeMcpJson(filePath, servers) {
|
|
@@ -122,9 +102,6 @@ export function updateWorkspaceMcpExtension(workdir, name, patch) {
|
|
|
122
102
|
writeMcpJson(mcpPath, servers);
|
|
123
103
|
return true;
|
|
124
104
|
}
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
// Unified listing
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
105
|
export function listAllMcpExtensions(workdir) {
|
|
129
106
|
const global = loadGlobalMcpExtensions();
|
|
130
107
|
const workspace = workdir ? loadWorkspaceMcpExtensions(workdir) : [];
|
|
@@ -138,9 +115,6 @@ export function listAllMcpExtensions(workdir) {
|
|
|
138
115
|
}
|
|
139
116
|
return [...global, ...workspace, ...claudeMcp];
|
|
140
117
|
}
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
// Catalog — merged recommended + installed with state computation
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
118
|
function cmdSummary(config) {
|
|
145
119
|
if (config.type === 'http' && config.url)
|
|
146
120
|
return config.url;
|
|
@@ -148,13 +122,6 @@ function cmdSummary(config) {
|
|
|
148
122
|
const args = (config.args || []).filter(a => a !== '-y');
|
|
149
123
|
return [cmd, ...args].join(' ').trim();
|
|
150
124
|
}
|
|
151
|
-
/**
|
|
152
|
-
* Generic @modelcontextprotocol/server-* demos that historically shipped in the
|
|
153
|
-
* recommended list but were later removed (no product identity, overlap with
|
|
154
|
-
* built-in agent capabilities like search/time). We hide them from the catalog
|
|
155
|
-
* UI so old installs don't clutter the Connected section. The configs are kept
|
|
156
|
-
* in setting.json untouched — users can still edit by hand if they want.
|
|
157
|
-
*/
|
|
158
125
|
const HIDDEN_GENERIC_DEMO_PACKAGES = new Set([
|
|
159
126
|
'@modelcontextprotocol/server-time',
|
|
160
127
|
'@modelcontextprotocol/server-fetch',
|
|
@@ -198,23 +165,12 @@ function computeStateForInstalled(config, auth, id, unhealthyIds) {
|
|
|
198
165
|
return 'unhealthy';
|
|
199
166
|
return 'ready';
|
|
200
167
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Produce the unified catalog for the dashboard: every recommended registry
|
|
203
|
-
* entry, plus any custom installed entries the user added, each with a
|
|
204
|
-
* computed state field.
|
|
205
|
-
*
|
|
206
|
-
* When `scope` is provided, recommended entries are filtered to those whose
|
|
207
|
-
* `recommendedScope` matches (or is `'both'`). Custom entries are filtered
|
|
208
|
-
* by where they are installed — `scope: 'global'` excludes workspace entries
|
|
209
|
-
* and vice versa.
|
|
210
|
-
*/
|
|
211
168
|
export function getCatalogItems(opts = {}) {
|
|
212
169
|
const recommended = getRecommendedMcpServers();
|
|
213
170
|
const installed = [
|
|
214
171
|
...loadGlobalMcpExtensions(),
|
|
215
172
|
...(opts.workdir ? loadWorkspaceMcpExtensions(opts.workdir) : []),
|
|
216
173
|
];
|
|
217
|
-
// Build lookup: catalogId -> installed entry (preferring global).
|
|
218
174
|
const installedByCatalogId = new Map();
|
|
219
175
|
const customEntries = [];
|
|
220
176
|
for (const entry of installed) {
|
|
@@ -240,9 +196,6 @@ export function getCatalogItems(opts = {}) {
|
|
|
240
196
|
return entry.scope === opts.scope;
|
|
241
197
|
};
|
|
242
198
|
const items = [];
|
|
243
|
-
// Builtin items derive their installed/state from a top-level config flag
|
|
244
|
-
// rather than `extensions.mcp`. Each catalogId maps to one flag — extend the
|
|
245
|
-
// switch when adding a new builtin.
|
|
246
199
|
const userConfig = loadUserConfig();
|
|
247
200
|
const builtinInstalled = (catalogId) => {
|
|
248
201
|
if (catalogId === 'pikiloom-browser')
|
|
@@ -251,7 +204,6 @@ export function getCatalogItems(opts = {}) {
|
|
|
251
204
|
return userConfig.peekabooEnabled === true;
|
|
252
205
|
return false;
|
|
253
206
|
};
|
|
254
|
-
// 1. Registry entries — preserve registry ordering.
|
|
255
207
|
for (const rec of recommended) {
|
|
256
208
|
if (!scopeMatchesRec(rec))
|
|
257
209
|
continue;
|
|
@@ -265,8 +217,6 @@ export function getCatalogItems(opts = {}) {
|
|
|
265
217
|
installed = builtinInstalled(rec.id);
|
|
266
218
|
state = installed ? 'ready' : 'recommended';
|
|
267
219
|
installedKey = installed ? rec.id : undefined;
|
|
268
|
-
// Leave scope undefined: builtins aren't tied to global/workspace
|
|
269
|
-
// storage, and the catalog UI's scope filter ignores items with no scope.
|
|
270
220
|
scope = undefined;
|
|
271
221
|
}
|
|
272
222
|
else {
|
|
@@ -299,7 +249,6 @@ export function getCatalogItems(opts = {}) {
|
|
|
299
249
|
isBuiltin: rec.isBuiltin,
|
|
300
250
|
});
|
|
301
251
|
}
|
|
302
|
-
// 2. Custom entries — user-added servers not in the recommended registry.
|
|
303
252
|
for (const entry of customEntries) {
|
|
304
253
|
if (!scopeMatchesEntry(entry))
|
|
305
254
|
continue;
|
|
@@ -331,10 +280,6 @@ export function getCatalogItems(opts = {}) {
|
|
|
331
280
|
export function getCatalogItem(id, opts = {}) {
|
|
332
281
|
return getCatalogItems(opts).find(i => i.id === id);
|
|
333
282
|
}
|
|
334
|
-
/**
|
|
335
|
-
* Build an `McpServerConfig` from a recommended entry plus user-supplied
|
|
336
|
-
* credentials. Used when installing a recommended server via the catalog flow.
|
|
337
|
-
*/
|
|
338
283
|
export function buildInstalledConfigFromRecommended(rec, opts = { enabled: false }) {
|
|
339
284
|
const creds = opts.credentials || {};
|
|
340
285
|
if (rec.transport.type === 'stdio') {
|
|
@@ -355,8 +300,6 @@ export function buildInstalledConfigFromRecommended(rec, opts = { enabled: false
|
|
|
355
300
|
}
|
|
356
301
|
const headers = {};
|
|
357
302
|
if (rec.auth.type === 'credentials') {
|
|
358
|
-
// Convention: first non-empty credential becomes Authorization: Bearer <value>.
|
|
359
|
-
// Matches how Stripe, Perplexity, and similar providers expect the token.
|
|
360
303
|
const first = rec.auth.fields.find(f => creds[f.key]);
|
|
361
304
|
if (first)
|
|
362
305
|
headers.Authorization = `Bearer ${creds[first.key]}`;
|
|
@@ -369,18 +312,8 @@ export function buildInstalledConfigFromRecommended(rec, opts = { enabled: false
|
|
|
369
312
|
catalogId: rec.id,
|
|
370
313
|
};
|
|
371
314
|
}
|
|
372
|
-
// ---------------------------------------------------------------------------
|
|
373
|
-
// Merge for session — called by bridge.ts
|
|
374
|
-
// ---------------------------------------------------------------------------
|
|
375
|
-
/**
|
|
376
|
-
* Build the merged MCP server list for a session.
|
|
377
|
-
* Priority (low → high): global → workspace .mcp.json → .claude/.mcp.json → ~/.claude/.mcp.json → builtins.
|
|
378
|
-
* Disabled servers are filtered out. OAuth Bearer headers are injected for
|
|
379
|
-
* any http-type server that has a valid token in the token store.
|
|
380
|
-
*/
|
|
381
315
|
export function mergeExtensionsForSession(builtinServers, workdir) {
|
|
382
316
|
const merged = {};
|
|
383
|
-
// 1. Global extensions from setting.json (lowest priority)
|
|
384
317
|
const userConfig = loadUserConfig();
|
|
385
318
|
const globalMcp = userConfig.extensions?.mcp;
|
|
386
319
|
if (globalMcp) {
|
|
@@ -406,7 +339,6 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
|
|
|
406
339
|
}
|
|
407
340
|
}
|
|
408
341
|
}
|
|
409
|
-
// 2. Workspace .mcp.json files (overwrite global)
|
|
410
342
|
if (workdir) {
|
|
411
343
|
for (const candidate of [
|
|
412
344
|
path.join(workdir, '.mcp.json'),
|
|
@@ -428,10 +360,9 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
|
|
|
428
360
|
}
|
|
429
361
|
}
|
|
430
362
|
}
|
|
431
|
-
catch {
|
|
363
|
+
catch { }
|
|
432
364
|
}
|
|
433
365
|
}
|
|
434
|
-
// 3. Built-in servers (highest priority)
|
|
435
366
|
for (const server of builtinServers) {
|
|
436
367
|
merged[server.name] = {
|
|
437
368
|
type: 'stdio',
|
|
@@ -440,7 +371,6 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
|
|
|
440
371
|
...(server.env ? { env: server.env } : {}),
|
|
441
372
|
};
|
|
442
373
|
}
|
|
443
|
-
// Filter out any remaining disabled entries
|
|
444
374
|
for (const [name, cfg] of Object.entries(merged)) {
|
|
445
375
|
if (cfg?.disabled === true || cfg?.enabled === false) {
|
|
446
376
|
delete merged[name];
|
|
@@ -448,15 +378,6 @@ export function mergeExtensionsForSession(builtinServers, workdir) {
|
|
|
448
378
|
}
|
|
449
379
|
return merged;
|
|
450
380
|
}
|
|
451
|
-
/**
|
|
452
|
-
* Convert global + workspace extensions to RegisteredMcpServer[] for Codex
|
|
453
|
-
* and Gemini agents that consume server arrays instead of merged configs.
|
|
454
|
-
*
|
|
455
|
-
* Supports both stdio and HTTP transports. For HTTP entries, OAuth Bearer
|
|
456
|
-
* headers are injected from the token store (same path as
|
|
457
|
-
* mergeExtensionsForSession), so a one-time global authorization carries
|
|
458
|
-
* across every workspace.
|
|
459
|
-
*/
|
|
460
381
|
export function getGlobalExtensionsAsServers(workdir) {
|
|
461
382
|
const merged = new Map();
|
|
462
383
|
const toEntry = (name, cfg) => {
|
|
@@ -615,7 +536,7 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
615
536
|
try {
|
|
616
537
|
child.stdin?.write(toolsHeader + toolsRequest);
|
|
617
538
|
}
|
|
618
|
-
catch {
|
|
539
|
+
catch { }
|
|
619
540
|
setTimeout(() => {
|
|
620
541
|
stopChildTree();
|
|
621
542
|
const tools = [];
|
|
@@ -636,11 +557,11 @@ export async function checkMcpHealth(config, timeoutMs = 10_000) {
|
|
|
636
557
|
tools.push(tool.name);
|
|
637
558
|
}
|
|
638
559
|
}
|
|
639
|
-
catch {
|
|
560
|
+
catch { }
|
|
640
561
|
}
|
|
641
562
|
}
|
|
642
563
|
}
|
|
643
|
-
catch {
|
|
564
|
+
catch { }
|
|
644
565
|
finish({ ok: true, tools: tools.length ? tools : undefined, elapsedMs: Date.now() - start });
|
|
645
566
|
}, 1500);
|
|
646
567
|
}, 100);
|
package/dist/agent/mcp/oauth.js
CHANGED
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP OAuth 2.1 + Dynamic Client Registration subsystem.
|
|
3
|
-
*
|
|
4
|
-
* Implements the MCP auth spec flow for remote HTTP MCP servers:
|
|
5
|
-
* 1. Discover auth server (Protected Resource Metadata + WWW-Authenticate header).
|
|
6
|
-
* 2. Fetch Authorization Server Metadata.
|
|
7
|
-
* 3. Register client dynamically (RFC 7591) if no pre-registered client_id.
|
|
8
|
-
* 4. Drive authorization_code + PKCE — returns auth URL for user's browser.
|
|
9
|
-
* 5. Exchange code for tokens on callback.
|
|
10
|
-
* 6. Refresh tokens when expired.
|
|
11
|
-
*
|
|
12
|
-
* Tokens are persisted via the token store (setting.json `extensions.mcpTokens`).
|
|
13
|
-
* State for in-flight authorizations is kept in memory only.
|
|
14
|
-
*/
|
|
15
1
|
import crypto from 'node:crypto';
|
|
16
2
|
import { loadUserConfig, saveUserConfig, } from '../../core/config/user-config.js';
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
// Token store (setting.json-backed)
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
3
|
export function getMcpToken(serverId) {
|
|
21
4
|
const cfg = loadUserConfig();
|
|
22
5
|
return cfg.extensions?.mcpTokens?.[serverId];
|
|
@@ -45,18 +28,12 @@ export function hasValidMcpToken(serverId) {
|
|
|
45
28
|
return false;
|
|
46
29
|
return true;
|
|
47
30
|
}
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
// PKCE helpers
|
|
50
|
-
// ---------------------------------------------------------------------------
|
|
51
31
|
function randomUrlSafe(bytes) {
|
|
52
32
|
return crypto.randomBytes(bytes).toString('base64url');
|
|
53
33
|
}
|
|
54
34
|
function pkceChallenge(verifier) {
|
|
55
35
|
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
56
36
|
}
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Discovery
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
37
|
async function fetchJson(url, init, timeoutMs = 8_000) {
|
|
61
38
|
const controller = new AbortController();
|
|
62
39
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -70,17 +47,6 @@ async function fetchJson(url, init, timeoutMs = 8_000) {
|
|
|
70
47
|
clearTimeout(timer);
|
|
71
48
|
}
|
|
72
49
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Discover the authorization server for a remote MCP resource and the
|
|
75
|
-
* canonical `resource` indicator the AS will validate against.
|
|
76
|
-
*
|
|
77
|
-
* Strategy:
|
|
78
|
-
* 1. Try GET {resourceBase}/.well-known/oauth-protected-resource (RFC 9728).
|
|
79
|
-
* 2. Fall back to probing the MCP endpoint for a 401 with WWW-Authenticate
|
|
80
|
-
* and follow the resource_metadata link.
|
|
81
|
-
* 3. Last resort: treat the origin as the AS and the input URL as the
|
|
82
|
-
* canonical resource.
|
|
83
|
-
*/
|
|
84
50
|
async function discoverAuthorizationServer(resourceUrl) {
|
|
85
51
|
const origin = new URL(resourceUrl).origin;
|
|
86
52
|
const adopt = (meta) => {
|
|
@@ -92,15 +58,13 @@ async function discoverAuthorizationServer(resourceUrl) {
|
|
|
92
58
|
canonicalResource: meta.resource || resourceUrl,
|
|
93
59
|
};
|
|
94
60
|
};
|
|
95
|
-
// Strategy 1: Protected Resource Metadata at the origin
|
|
96
61
|
try {
|
|
97
62
|
const meta = await fetchJson(`${origin}/.well-known/oauth-protected-resource`);
|
|
98
63
|
const out = adopt(meta);
|
|
99
64
|
if (out)
|
|
100
65
|
return out;
|
|
101
66
|
}
|
|
102
|
-
catch {
|
|
103
|
-
// Strategy 2: probe resource for WWW-Authenticate, follow resource_metadata
|
|
67
|
+
catch { }
|
|
104
68
|
try {
|
|
105
69
|
const controller = new AbortController();
|
|
106
70
|
const timer = setTimeout(() => controller.abort(), 8_000);
|
|
@@ -113,9 +77,6 @@ async function discoverAuthorizationServer(resourceUrl) {
|
|
|
113
77
|
clearTimeout(timer);
|
|
114
78
|
if (res.status === 401) {
|
|
115
79
|
const auth = res.headers.get('www-authenticate') || '';
|
|
116
|
-
// A header may declare resource_metadata twice; prefer the most specific
|
|
117
|
-
// (last) match since servers commonly emit both origin-level and
|
|
118
|
-
// per-resource paths in one header.
|
|
119
80
|
const matches = [...auth.matchAll(/resource_metadata="([^"]+)"/gi)];
|
|
120
81
|
const link = matches.length ? matches[matches.length - 1][1] : null;
|
|
121
82
|
if (link) {
|
|
@@ -126,12 +87,10 @@ async function discoverAuthorizationServer(resourceUrl) {
|
|
|
126
87
|
}
|
|
127
88
|
}
|
|
128
89
|
}
|
|
129
|
-
catch {
|
|
130
|
-
// Last resort: assume the resource origin is itself the authorization server
|
|
90
|
+
catch { }
|
|
131
91
|
return { authorizationServer: origin, canonicalResource: resourceUrl };
|
|
132
92
|
}
|
|
133
93
|
async function fetchAuthorizationServerMetadata(issuer) {
|
|
134
|
-
// Try OAuth AS metadata first (RFC 8414), then OIDC fallback
|
|
135
94
|
const candidates = [
|
|
136
95
|
`${issuer.replace(/\/$/, '')}/.well-known/oauth-authorization-server`,
|
|
137
96
|
`${issuer.replace(/\/$/, '')}/.well-known/openid-configuration`,
|
|
@@ -183,9 +142,6 @@ async function resolveEndpoints(auth, resourceUrl) {
|
|
|
183
142
|
canonicalResource: discovered.canonicalResource,
|
|
184
143
|
};
|
|
185
144
|
}
|
|
186
|
-
// ---------------------------------------------------------------------------
|
|
187
|
-
// Pending state (in-memory, short-lived)
|
|
188
|
-
// ---------------------------------------------------------------------------
|
|
189
145
|
const pendingFlows = new Map();
|
|
190
146
|
const PENDING_TTL_MS = 10 * 60 * 1000;
|
|
191
147
|
function sweepPending() {
|
|
@@ -195,18 +151,6 @@ function sweepPending() {
|
|
|
195
151
|
pendingFlows.delete(state);
|
|
196
152
|
}
|
|
197
153
|
}
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
// Public API: start authorization, handle callback, refresh
|
|
200
|
-
// ---------------------------------------------------------------------------
|
|
201
|
-
/**
|
|
202
|
-
* Kick off an OAuth flow for the given MCP server.
|
|
203
|
-
*
|
|
204
|
-
* @param serverId The catalog id / installed-extension key.
|
|
205
|
-
* @param auth The auth spec from registry.
|
|
206
|
-
* @param resourceUrl The MCP server URL (http transport url).
|
|
207
|
-
* @param redirectUri Callback URL (http://localhost:<port>/api/extensions/mcp/oauth/callback).
|
|
208
|
-
* @param clientName Human-readable client name shown to the provider.
|
|
209
|
-
*/
|
|
210
154
|
export async function startAuthorization(opts) {
|
|
211
155
|
sweepPending();
|
|
212
156
|
const { serverId, auth, resourceUrl, redirectUri, clientName } = opts;
|
|
@@ -254,9 +198,6 @@ export async function startAuthorization(opts) {
|
|
|
254
198
|
});
|
|
255
199
|
return { authUrl, state };
|
|
256
200
|
}
|
|
257
|
-
/**
|
|
258
|
-
* Exchange the authorization code for tokens and persist the result.
|
|
259
|
-
*/
|
|
260
201
|
export async function completeAuthorization(opts) {
|
|
261
202
|
const flow = pendingFlows.get(opts.state);
|
|
262
203
|
if (!flow)
|
|
@@ -298,10 +239,6 @@ export async function completeAuthorization(opts) {
|
|
|
298
239
|
saveMcpToken(flow.serverId, record);
|
|
299
240
|
return { ok: true, serverId: flow.serverId };
|
|
300
241
|
}
|
|
301
|
-
/**
|
|
302
|
-
* Refresh an expired token. Returns the updated record or null if refresh failed
|
|
303
|
-
* (e.g., no refresh_token available, or refresh rejected).
|
|
304
|
-
*/
|
|
305
242
|
export async function refreshMcpToken(serverId) {
|
|
306
243
|
const token = getMcpToken(serverId);
|
|
307
244
|
if (!token?.refreshToken)
|
|
@@ -338,10 +275,6 @@ export async function refreshMcpToken(serverId) {
|
|
|
338
275
|
return null;
|
|
339
276
|
}
|
|
340
277
|
}
|
|
341
|
-
/**
|
|
342
|
-
* Inject Authorization: Bearer headers for remote MCP configs that have a token.
|
|
343
|
-
* Called by bridge.ts before handing merged config to agents.
|
|
344
|
-
*/
|
|
345
278
|
export function injectOAuthHeaders(name, config) {
|
|
346
279
|
const token = getMcpToken(name);
|
|
347
280
|
if (!token?.accessToken)
|
|
@@ -1,30 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP extension registry — curated recommended servers, recommended skills, community search.
|
|
3
|
-
*
|
|
4
|
-
* Schema supports three transport × auth combinations:
|
|
5
|
-
* - stdio + none (local, zero-config)
|
|
6
|
-
* - stdio + credentials (local, needs API key/token)
|
|
7
|
-
* - http + none (rare)
|
|
8
|
-
* - http + credentials (remote with API key)
|
|
9
|
-
* - http + mcp-oauth (remote SaaS via MCP OAuth spec)
|
|
10
|
-
*
|
|
11
|
-
* For MCP-OAuth servers, `authorizationEndpoint` / `tokenEndpoint` can be
|
|
12
|
-
* pre-declared (fast path) or omitted (discovered via MCP Protected Resource
|
|
13
|
-
* Metadata at `<url>/.well-known/oauth-protected-resource`).
|
|
14
|
-
*/
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Recommended MCP servers + skill repos — data lives in src/catalog/
|
|
17
|
-
//
|
|
18
|
-
// This module owns the *types* and helper functions. Edit
|
|
19
|
-
// `src/catalog/mcp-servers.ts` and `src/catalog/skill-repos.ts` to add or hide
|
|
20
|
-
// entries; the arrays below are just pointers back at that catalog.
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
1
|
import { MCP_SERVERS, SKILL_REPOS } from '../../catalog/index.js';
|
|
23
2
|
const RECOMMENDED_MCP_SERVERS = MCP_SERVERS;
|
|
24
3
|
const RECOMMENDED_SKILL_REPOS = SKILL_REPOS;
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Public API
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
4
|
export function getRecommendedMcpServers() {
|
|
29
5
|
return RECOMMENDED_MCP_SERVERS;
|
|
30
6
|
}
|
|
@@ -34,10 +10,6 @@ export function getRecommendedMcpServer(id) {
|
|
|
34
10
|
export function getRecommendedSkillRepos() {
|
|
35
11
|
return RECOMMENDED_SKILL_REPOS;
|
|
36
12
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Search the official MCP Registry API for servers.
|
|
39
|
-
* Falls back to npm search if the registry is unreachable.
|
|
40
|
-
*/
|
|
41
13
|
export async function searchMcpServers(query, limit = 20) {
|
|
42
14
|
if (!query.trim())
|
|
43
15
|
return [];
|
|
@@ -87,12 +59,9 @@ export async function searchMcpServers(query, limit = 20) {
|
|
|
87
59
|
}
|
|
88
60
|
}
|
|
89
61
|
}
|
|
90
|
-
catch {
|
|
62
|
+
catch { }
|
|
91
63
|
return [];
|
|
92
64
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Search for skills via npm search (`agent skill` keywords).
|
|
95
|
-
*/
|
|
96
65
|
export async function searchSkills(query, limit = 20) {
|
|
97
66
|
if (!query.trim())
|
|
98
67
|
return [];
|
|
@@ -114,6 +83,6 @@ export async function searchSkills(query, limit = 20) {
|
|
|
114
83
|
})).filter((s) => s.name);
|
|
115
84
|
}
|
|
116
85
|
}
|
|
117
|
-
catch {
|
|
86
|
+
catch { }
|
|
118
87
|
return [];
|
|
119
88
|
}
|
|
@@ -1,29 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mcp-session-server.ts — MCP server process for pikiloom session bridge.
|
|
3
|
-
*
|
|
4
|
-
* Spawned by the agent CLI (claude/codex/gemini) via --mcp-config or codex mcp add.
|
|
5
|
-
* Communicates with the agent over stdio using the MCP protocol (JSON-RPC 2.0).
|
|
6
|
-
*
|
|
7
|
-
* Supports two stdio transports (auto-detected from first byte):
|
|
8
|
-
* - Content-Length framing (Claude, Gemini — standard MCP/LSP)
|
|
9
|
-
* - Newline-delimited JSON (Codex)
|
|
10
|
-
*
|
|
11
|
-
* Context is injected via environment variables:
|
|
12
|
-
* MCP_WORKSPACE_PATH — absolute path to the session workspace
|
|
13
|
-
* MCP_STAGED_FILES — JSON array of staged file relative paths
|
|
14
|
-
* MCP_CALLBACK_URL — HTTP URL for the pikiloom callback server
|
|
15
|
-
*
|
|
16
|
-
* Tools are defined in src/tools/ — each module exports definitions + handlers.
|
|
17
|
-
*/
|
|
18
1
|
import path from 'node:path';
|
|
19
2
|
import { createRetainedLogSink, writeScopedLog } from '../../core/logging.js';
|
|
20
3
|
import { workspaceTools } from './tools/workspace.js';
|
|
21
4
|
import { goalTools } from './tools/goal.js';
|
|
22
5
|
import { awaitResumeTools } from './tools/await-resume.js';
|
|
23
6
|
import { askUserTools } from './tools/ask-user.js';
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Logging — writes to stderr + file so it doesn't interfere with stdio MCP transport
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
7
|
const _logSink = (() => {
|
|
28
8
|
try {
|
|
29
9
|
const ws = process.env.MCP_WORKSPACE_PATH || '';
|
|
@@ -53,9 +33,6 @@ function summarizeArgs(args, max = 200) {
|
|
|
53
33
|
return '{}';
|
|
54
34
|
return text.length <= max ? text : `${text.slice(0, Math.max(0, max - 3)).trimEnd()}...`;
|
|
55
35
|
}
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
// Context from environment
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
36
|
const ctx = {
|
|
60
37
|
workspace: process.env.MCP_WORKSPACE_PATH || '',
|
|
61
38
|
workdir: process.env.MCP_WORKDIR || undefined,
|
|
@@ -70,35 +47,21 @@ const ctx = {
|
|
|
70
47
|
callbackUrl: process.env.MCP_CALLBACK_URL || '',
|
|
71
48
|
};
|
|
72
49
|
log(`started workspace=${ctx.workspace} stagedFiles=${ctx.stagedFiles.length} callbackUrl=${ctx.callbackUrl ? 'set' : 'MISSING'}`);
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
// Tool registry — collect all tool modules
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// `MCP_TOOLS_AVAILABLE` lists tool families the bridge has wired up. Codex
|
|
77
|
-
// has a native `/goal` implementation and native user-input, so it skips
|
|
78
|
-
// `goalTools` and never receives `ask-user` via the bridge.
|
|
79
50
|
const AVAILABLE = new Set((process.env.MCP_TOOLS_AVAILABLE || '').split(',').map(s => s.trim()).filter(Boolean));
|
|
80
51
|
const IS_CODEX = process.env.MCP_AGENT === 'codex';
|
|
81
52
|
const TOOL_MODULES = [
|
|
82
53
|
...(AVAILABLE.has('workspace') ? [workspaceTools] : []),
|
|
83
54
|
...(IS_CODEX ? [] : [goalTools]),
|
|
84
|
-
// Codex parks/resumes via its own native goal machinery; the pikiloom
|
|
85
|
-
// awaiting marker is for the `claude -p`-style drivers whose turn process
|
|
86
|
-
// exits at `result`.
|
|
87
55
|
...(IS_CODEX ? [] : [awaitResumeTools]),
|
|
88
56
|
...(AVAILABLE.has('ask-user') ? [askUserTools] : []),
|
|
89
57
|
];
|
|
90
58
|
const ALL_TOOLS = TOOL_MODULES.flatMap(m => m.tools);
|
|
91
|
-
/** Lookup: tool name → module that handles it. */
|
|
92
59
|
const TOOL_HANDLERS = new Map();
|
|
93
60
|
for (const mod of TOOL_MODULES) {
|
|
94
61
|
for (const t of mod.tools) {
|
|
95
62
|
TOOL_HANDLERS.set(t.name, mod);
|
|
96
63
|
}
|
|
97
64
|
}
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
// MCP protocol — auto-detect transport format
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
/** 'framed' = Content-Length (Claude/Gemini), 'ndjson' = newline-delimited (Codex) */
|
|
102
65
|
let transport = null;
|
|
103
66
|
function send(msg) {
|
|
104
67
|
const body = JSON.stringify(msg);
|
|
@@ -115,9 +78,6 @@ function respond(id, result) {
|
|
|
115
78
|
function respondError(id, code, message) {
|
|
116
79
|
send({ jsonrpc: '2.0', id, error: { code, message } });
|
|
117
80
|
}
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// Stdio reader — auto-detecting Content-Length framed vs NDJSON
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
81
|
let buffer = '';
|
|
122
82
|
function processFramed() {
|
|
123
83
|
while (true) {
|
|
@@ -139,7 +99,7 @@ function processFramed() {
|
|
|
139
99
|
try {
|
|
140
100
|
handleMessage(JSON.parse(body));
|
|
141
101
|
}
|
|
142
|
-
catch {
|
|
102
|
+
catch { }
|
|
143
103
|
}
|
|
144
104
|
}
|
|
145
105
|
function processNdjson() {
|
|
@@ -154,7 +114,7 @@ function processNdjson() {
|
|
|
154
114
|
try {
|
|
155
115
|
handleMessage(JSON.parse(line));
|
|
156
116
|
}
|
|
157
|
-
catch {
|
|
117
|
+
catch { }
|
|
158
118
|
}
|
|
159
119
|
}
|
|
160
120
|
function processBuffer() {
|
|
@@ -175,9 +135,6 @@ process.stdin.on('data', (chunk) => {
|
|
|
175
135
|
processBuffer();
|
|
176
136
|
});
|
|
177
137
|
process.stdin.on('end', () => process.exit(0));
|
|
178
|
-
// ---------------------------------------------------------------------------
|
|
179
|
-
// Message dispatcher
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
138
|
function handleMessage(msg) {
|
|
182
139
|
const { id, method, params } = msg;
|
|
183
140
|
switch (method) {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tools/ask-user.ts — im_ask_user: block the current turn and route a question
|
|
3
|
-
* to the user via IM or dashboard. Used in place of Claude's native
|
|
4
|
-
* `AskUserQuestion`, which is unusable in `-p` mode.
|
|
5
|
-
*/
|
|
6
1
|
import http from 'node:http';
|
|
7
2
|
import { toolResult, toolLog } from './types.js';
|
|
8
3
|
const tools = [
|
|
@@ -97,7 +92,6 @@ function callbackAskUser(callbackUrl, body) {
|
|
|
97
92
|
});
|
|
98
93
|
});
|
|
99
94
|
req.on('error', e => reject(e));
|
|
100
|
-
// User reply has no upper bound — never time out the socket.
|
|
101
95
|
req.setTimeout(0);
|
|
102
96
|
req.write(payload);
|
|
103
97
|
req.end();
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tools/await-resume.ts — "waiting for background work" marker MCP tool.
|
|
3
|
-
*
|
|
4
|
-
* await_background — Lets the model declare that this turn is ending while
|
|
5
|
-
* detached/background work it launched keeps running, and
|
|
6
|
-
* it intends to report back later. Purely a UI hint: it
|
|
7
|
-
* changes nothing about execution, it only lets the
|
|
8
|
-
* dashboard show a "waiting" state instead of "completed"
|
|
9
|
-
* for the interval until the session next runs.
|
|
10
|
-
*
|
|
11
|
-
* State lives at <sessionRoot>/awaiting.json; this server resolves the session
|
|
12
|
-
* root from MCP_WORKSPACE_PATH (which points to <sessionRoot>/workspace). The
|
|
13
|
-
* parent reads & clears it (see agent/await-resume.ts) — clearing happens
|
|
14
|
-
* automatically the next time the session runs.
|
|
15
|
-
*/
|
|
16
1
|
import fs from 'node:fs';
|
|
17
2
|
import path from 'node:path';
|
|
18
3
|
import { toolResult, toolLog } from './types.js';
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tools/goal.ts — Persistent thread goal MCP tools.
|
|
3
|
-
*
|
|
4
|
-
* goal_get — Returns the current goal (objective, status, usage, budget).
|
|
5
|
-
* goal_update — Lets the model mark the goal complete after a completion audit.
|
|
6
|
-
* The schema only accepts `status: "complete"`. Pause / resume /
|
|
7
|
-
* clear / budget_limited transitions are user- or runtime-controlled
|
|
8
|
-
* and not exposed to the model.
|
|
9
|
-
*
|
|
10
|
-
* Goal state lives at <sessionRoot>/goal.json; this server resolves the session
|
|
11
|
-
* root from MCP_WORKSPACE_PATH (which points to <sessionRoot>/workspace).
|
|
12
|
-
*/
|
|
13
1
|
import fs from 'node:fs';
|
|
14
2
|
import path from 'node:path';
|
|
15
3
|
import { toolResult, toolLog } from './types.js';
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
// Tool definitions — descriptions match Codex's analogous `get_goal` / `update_goal`
|
|
18
|
-
// behaviour but use the pikiloom tool name.
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
4
|
const tools = [
|
|
21
5
|
{
|
|
22
6
|
name: 'goal_get',
|
|
@@ -46,9 +30,6 @@ const tools = [
|
|
|
46
30
|
},
|
|
47
31
|
},
|
|
48
32
|
];
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Path resolution — workspace → session root → goal.json
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
33
|
function sessionRootFromCtx(ctx) {
|
|
53
34
|
const workspace = path.resolve(ctx.workspace || '');
|
|
54
35
|
if (!workspace)
|
|
@@ -91,9 +72,6 @@ function serialize(goal) {
|
|
|
91
72
|
updated_at: goal.updatedAt,
|
|
92
73
|
};
|
|
93
74
|
}
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
|
-
// Handlers
|
|
96
|
-
// ---------------------------------------------------------------------------
|
|
97
75
|
function handleGoalGet(ctx) {
|
|
98
76
|
const file = goalPathFromCtx(ctx);
|
|
99
77
|
toolLog('goal_get', `file=${file || '(unresolved)'}`);
|
|
@@ -129,9 +107,6 @@ function handleGoalUpdate(args, ctx) {
|
|
|
129
107
|
: `tokens used: ${goal.tokensUsed}`,
|
|
130
108
|
}, null, 2));
|
|
131
109
|
}
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
// Module export
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
110
|
export const goalTools = {
|
|
136
111
|
tools,
|
|
137
112
|
handle(name, args, ctx) {
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP tool type definitions and helper utilities.
|
|
3
|
-
*/
|
|
4
1
|
import { writeScopedLog } from '../../../core/logging.js';
|
|
5
|
-
/** Helper to build a text tool result. */
|
|
6
2
|
export function toolResult(text, isError = false) {
|
|
7
3
|
return { content: [{ type: 'text', text }], ...(isError ? { isError: true } : {}) };
|
|
8
4
|
}
|
|
9
|
-
/** Shared logger for tool modules — writes to stderr to avoid interfering with stdio MCP transport. */
|
|
10
5
|
export function toolLog(tool, msg) {
|
|
11
6
|
writeScopedLog(`tool:${tool}`, msg, { level: 'debug', stream: 'stderr' });
|
|
12
7
|
}
|