@sideboard-ai/core 0.1.311 → 0.1.312
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/dist/{agents-XXXRQJJO.js → agents-IOJRGGK3.js} +1 -1
- package/dist/{agents-QIIZYNOM.js → agents-XDI6QHBA.js} +1 -1
- package/dist/{chunk-GQJOLTZU.js → chunk-2P2HYXA2.js} +620 -18
- package/dist/{chunk-4YBQBVYL.js → chunk-4FZUUH6W.js} +514 -22
- package/dist/{chunk-7ORMOP3D.js → chunk-LLTWP4PK.js} +12 -2
- package/dist/{chunk-2ER7DQJP.js → chunk-VIVXKN5N.js} +19 -3
- package/dist/index.cjs +743 -384
- package/dist/index.d.cts +42 -8
- package/dist/index.d.ts +42 -8
- package/dist/index.js +27 -216
- package/dist/mcp/run-stdio.cjs +759 -116
- package/dist/mcp/run-stdio.js +2 -2
- package/dist/{orchestrator-2CCFGYAA.js → orchestrator-BYTSILLH.js} +2 -2
- package/dist/{orchestrator-3W5S6TNP.js → orchestrator-S44BMNN7.js} +2 -2
- package/package.json +1 -1
|
@@ -513,6 +513,9 @@ var brightsyAdapter = {
|
|
|
513
513
|
|
|
514
514
|
// src/agents/claude.ts
|
|
515
515
|
import { existsSync as existsSync7 } from "fs";
|
|
516
|
+
|
|
517
|
+
// src/mcp-ui/host.ts
|
|
518
|
+
import { createHash } from "crypto";
|
|
516
519
|
import { homedir as homedir4 } from "os";
|
|
517
520
|
|
|
518
521
|
// src/agents/orch-mcp-isolation.ts
|
|
@@ -608,18 +611,18 @@ function mcpUiContentSecurityPolicy(csp) {
|
|
|
608
611
|
const connect = (csp?.connectDomains ?? []).join(" ");
|
|
609
612
|
const frames = csp?.frameDomains?.length ? csp.frameDomains.join(" ") : "'none'";
|
|
610
613
|
const base = csp?.baseUriDomains?.length ? csp.baseUriDomains.join(" ") : "'none'";
|
|
614
|
+
const connectSrc = connect ? `'self' ${connect}`.trim() : "'none'";
|
|
611
615
|
return [
|
|
612
616
|
"default-src 'none'",
|
|
613
|
-
`script-src 'self' 'unsafe-inline'
|
|
617
|
+
`script-src 'self' 'unsafe-inline' ${resource}`.trim(),
|
|
614
618
|
`style-src 'self' 'unsafe-inline' ${resource}`.trim(),
|
|
615
|
-
`img-src 'self' data:
|
|
616
|
-
`font-src 'self'
|
|
617
|
-
`connect-src
|
|
618
|
-
`media-src 'self' data:
|
|
619
|
+
`img-src 'self' data: ${resource}`.trim(),
|
|
620
|
+
`font-src 'self' ${resource}`.trim(),
|
|
621
|
+
`connect-src ${connectSrc}`,
|
|
622
|
+
`media-src 'self' data: ${resource}`.trim(),
|
|
619
623
|
`frame-src ${frames}`,
|
|
620
624
|
`base-uri ${base}`,
|
|
621
|
-
"object-src 'none'"
|
|
622
|
-
"form-action 'self'"
|
|
625
|
+
"object-src 'none'"
|
|
623
626
|
].join("; ");
|
|
624
627
|
}
|
|
625
628
|
function htmlText(text, blob) {
|
|
@@ -656,8 +659,10 @@ function originList(value) {
|
|
|
656
659
|
return out;
|
|
657
660
|
}
|
|
658
661
|
function originToken(value) {
|
|
662
|
+
const trimmed = value.trim();
|
|
663
|
+
if (/^(https?|wss?):\/\/\*\.[a-z0-9.-]+(?::\d+)?$/i.test(trimmed)) return trimmed;
|
|
659
664
|
try {
|
|
660
|
-
const url = new URL(
|
|
665
|
+
const url = new URL(trimmed);
|
|
661
666
|
if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "wss:" && url.protocol !== "ws:") {
|
|
662
667
|
return null;
|
|
663
668
|
}
|
|
@@ -1493,6 +1498,497 @@ function toCodexUnattendedAppsArgs() {
|
|
|
1493
1498
|
];
|
|
1494
1499
|
}
|
|
1495
1500
|
|
|
1501
|
+
// src/mcp-ui/proxy-http.ts
|
|
1502
|
+
import { randomBytes, randomUUID } from "crypto";
|
|
1503
|
+
import { createServer } from "http";
|
|
1504
|
+
import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1505
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
1506
|
+
|
|
1507
|
+
// src/mcp-ui/proxy-server.ts
|
|
1508
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1509
|
+
import {
|
|
1510
|
+
CallToolRequestSchema,
|
|
1511
|
+
GetPromptRequestSchema,
|
|
1512
|
+
ListPromptsRequestSchema,
|
|
1513
|
+
ListResourcesRequestSchema,
|
|
1514
|
+
ListResourceTemplatesRequestSchema,
|
|
1515
|
+
ListToolsRequestSchema,
|
|
1516
|
+
ReadResourceRequestSchema
|
|
1517
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
1518
|
+
|
|
1519
|
+
// src/mcp-ui/visibility.ts
|
|
1520
|
+
function visibilityFromMeta(meta) {
|
|
1521
|
+
const rec = asRecord2(meta);
|
|
1522
|
+
const ui = asRecord2(rec.ui);
|
|
1523
|
+
const raw = ui.visibility;
|
|
1524
|
+
if (!Array.isArray(raw)) return ["model", "app"];
|
|
1525
|
+
const out = [];
|
|
1526
|
+
for (const item of raw) {
|
|
1527
|
+
if (item !== "model" && item !== "app") continue;
|
|
1528
|
+
if (!out.includes(item)) out.push(item);
|
|
1529
|
+
}
|
|
1530
|
+
return out.length ? out : ["model", "app"];
|
|
1531
|
+
}
|
|
1532
|
+
function toolVisibleTo(visibility, audience) {
|
|
1533
|
+
const vis = visibility?.length ? visibility : ["model", "app"];
|
|
1534
|
+
return vis.includes(audience);
|
|
1535
|
+
}
|
|
1536
|
+
function asRecord2(value) {
|
|
1537
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
1538
|
+
return value;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// src/mcp-ui/proxy-server.ts
|
|
1542
|
+
function attachMcpUiProxyHandlers(server, session) {
|
|
1543
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1544
|
+
const tools = await session.listTools();
|
|
1545
|
+
return { tools: tools.filter((tool) => toolVisibleTo(tool.visibility, "model")).map(listedTool) };
|
|
1546
|
+
});
|
|
1547
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1548
|
+
const name = request.params.name;
|
|
1549
|
+
const tools = await session.listTools();
|
|
1550
|
+
const tool = tools.find((item) => item.name === name);
|
|
1551
|
+
if (!tool || !toolVisibleTo(tool.visibility, "model")) {
|
|
1552
|
+
throw new Error(`Tool ${name} is not available to the model`);
|
|
1553
|
+
}
|
|
1554
|
+
return await session.callTool(name, request.params.arguments ?? {});
|
|
1555
|
+
});
|
|
1556
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
1557
|
+
const resource = await session.readResource(request.params.uri);
|
|
1558
|
+
if (!resource) throw new Error(`Resource ${request.params.uri} was empty`);
|
|
1559
|
+
return {
|
|
1560
|
+
contents: [
|
|
1561
|
+
{
|
|
1562
|
+
uri: resource.uri,
|
|
1563
|
+
...resource.mimeType ? { mimeType: resource.mimeType } : {},
|
|
1564
|
+
...resource.text != null ? { text: resource.text } : {},
|
|
1565
|
+
...resource.blob != null ? { blob: resource.blob } : {}
|
|
1566
|
+
}
|
|
1567
|
+
]
|
|
1568
|
+
};
|
|
1569
|
+
});
|
|
1570
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => session.listResources());
|
|
1571
|
+
server.setRequestHandler(
|
|
1572
|
+
ListResourceTemplatesRequestSchema,
|
|
1573
|
+
async () => session.listResourceTemplates()
|
|
1574
|
+
);
|
|
1575
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => session.listPrompts());
|
|
1576
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
1577
|
+
return await session.getPrompt(request.params.name, request.params.arguments);
|
|
1578
|
+
});
|
|
1579
|
+
server.fallbackRequestHandler = async (request) => {
|
|
1580
|
+
return await session.forward(request.method, request.params);
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
function listedTool(tool) {
|
|
1584
|
+
return {
|
|
1585
|
+
name: tool.name,
|
|
1586
|
+
...tool.description ? { description: tool.description } : {},
|
|
1587
|
+
inputSchema: tool.inputSchema ?? { type: "object", properties: {} },
|
|
1588
|
+
_meta: {
|
|
1589
|
+
ui: {
|
|
1590
|
+
...tool.resourceUri ? { resourceUri: tool.resourceUri } : {},
|
|
1591
|
+
visibility: tool.visibility
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// src/mcp-ui/proxy-http.ts
|
|
1598
|
+
async function listenMcpUiProxy(opts) {
|
|
1599
|
+
const token = randomBytes(24).toString("hex");
|
|
1600
|
+
const transports = /* @__PURE__ */ new Map();
|
|
1601
|
+
const httpServer = createServer((req, res) => {
|
|
1602
|
+
void handle(req, res);
|
|
1603
|
+
});
|
|
1604
|
+
await new Promise((resolve2, reject) => {
|
|
1605
|
+
httpServer.once("error", reject);
|
|
1606
|
+
httpServer.listen(0, "127.0.0.1", () => resolve2());
|
|
1607
|
+
});
|
|
1608
|
+
const addr = httpServer.address();
|
|
1609
|
+
if (!addr || typeof addr === "string") throw new Error("mcp-ui proxy failed to bind");
|
|
1610
|
+
const origin = `http://127.0.0.1:${addr.port}`;
|
|
1611
|
+
async function handle(req, res) {
|
|
1612
|
+
if (req.headers.authorization !== `Bearer ${token}`) {
|
|
1613
|
+
res.writeHead(401).end();
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
const target = parseMcpUiProxyPath(req.url);
|
|
1617
|
+
if (!target) {
|
|
1618
|
+
res.writeHead(404).end();
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
try {
|
|
1622
|
+
const session = await opts.sessionFor(target.server, target.dirsKey);
|
|
1623
|
+
if (!session) {
|
|
1624
|
+
res.writeHead(404).end();
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
const header = req.headers["mcp-session-id"];
|
|
1628
|
+
const sid = typeof header === "string" ? header : "";
|
|
1629
|
+
const sessionKey = `${target.server}:${target.dirsKey}`;
|
|
1630
|
+
let transport = sid ? transports.get(`${sessionKey}:${sid}`) : void 0;
|
|
1631
|
+
if (!transport) {
|
|
1632
|
+
if (req.method !== "POST") {
|
|
1633
|
+
res.writeHead(400).end();
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1636
|
+
const created = new StreamableHTTPServerTransport({
|
|
1637
|
+
sessionIdGenerator: () => randomUUID(),
|
|
1638
|
+
allowedHosts: ["127.0.0.1", "localhost"],
|
|
1639
|
+
onsessioninitialized: (id) => {
|
|
1640
|
+
transports.set(`${sessionKey}:${id}`, created);
|
|
1641
|
+
},
|
|
1642
|
+
onsessionclosed: (id) => {
|
|
1643
|
+
transports.delete(`${sessionKey}:${id}`);
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
transport = created;
|
|
1647
|
+
const mcp = new Server2(
|
|
1648
|
+
{ name: `sideboard-ui-proxy-${target.server}`, version: "1.0.0" },
|
|
1649
|
+
{ capabilities: session.capabilities() }
|
|
1650
|
+
);
|
|
1651
|
+
attachMcpUiProxyHandlers(mcp, session);
|
|
1652
|
+
await mcp.connect(created);
|
|
1653
|
+
}
|
|
1654
|
+
await transport.handleRequest(req, res);
|
|
1655
|
+
} catch {
|
|
1656
|
+
if (!res.headersSent) res.writeHead(500).end();
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
return {
|
|
1660
|
+
urlFor(server, dirsKey) {
|
|
1661
|
+
const base = `${origin}/s/${encodeURIComponent(server)}`;
|
|
1662
|
+
return dirsKey ? `${base}/d/${dirsKey}` : base;
|
|
1663
|
+
},
|
|
1664
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1665
|
+
async close() {
|
|
1666
|
+
await Promise.all([...transports.values()].map((item) => item.close().catch(() => void 0)));
|
|
1667
|
+
transports.clear();
|
|
1668
|
+
await new Promise((resolve2) => httpServer.close(() => resolve2()));
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
function parseMcpUiProxyPath(url) {
|
|
1673
|
+
const path = (url ?? "").split("?")[0] ?? "";
|
|
1674
|
+
if (!path.startsWith("/s/")) return null;
|
|
1675
|
+
const rest = path.slice(3).replace(/\/$/, "");
|
|
1676
|
+
const split = rest.indexOf("/d/");
|
|
1677
|
+
try {
|
|
1678
|
+
if (split < 0) {
|
|
1679
|
+
const server2 = decodeURIComponent(rest).trim();
|
|
1680
|
+
return server2 ? { server: server2, dirsKey: "" } : null;
|
|
1681
|
+
}
|
|
1682
|
+
const server = decodeURIComponent(rest.slice(0, split)).trim();
|
|
1683
|
+
const dirsKey = rest.slice(split + 3);
|
|
1684
|
+
if (!server || !/^[a-f0-9]{16}$/.test(dirsKey)) return null;
|
|
1685
|
+
return { server, dirsKey };
|
|
1686
|
+
} catch {
|
|
1687
|
+
return null;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
// src/mcp-ui/session.ts
|
|
1692
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
1693
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
1694
|
+
import { StdioClientTransport, getDefaultEnvironment } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
1695
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
1696
|
+
import { ResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
1697
|
+
var REQUEST_TIMEOUT_MS = 2e4;
|
|
1698
|
+
async function orEmpty(run2, fallback) {
|
|
1699
|
+
try {
|
|
1700
|
+
return await run2();
|
|
1701
|
+
} catch {
|
|
1702
|
+
return fallback;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
var UI_CAPABILITIES = {
|
|
1706
|
+
extensions: {
|
|
1707
|
+
"io.modelcontextprotocol/ui": {
|
|
1708
|
+
mimeTypes: ["text/html;profile=mcp-app"]
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
async function openMcpUiSession(launch) {
|
|
1713
|
+
return openMcpUiSessionFromTransport(transportForLaunch(launch));
|
|
1714
|
+
}
|
|
1715
|
+
async function openMcpUiSessionFromTransport(transport) {
|
|
1716
|
+
const client = new Client(
|
|
1717
|
+
{ name: "sideboard", version: "1.0.0" },
|
|
1718
|
+
{ capabilities: UI_CAPABILITIES }
|
|
1719
|
+
);
|
|
1720
|
+
await client.connect(transport, { timeout: REQUEST_TIMEOUT_MS });
|
|
1721
|
+
return {
|
|
1722
|
+
async listTools() {
|
|
1723
|
+
const listed = await client.listTools(void 0, { timeout: REQUEST_TIMEOUT_MS });
|
|
1724
|
+
return listed.tools.map((tool) => {
|
|
1725
|
+
const meta = tool._meta;
|
|
1726
|
+
return {
|
|
1727
|
+
name: tool.name,
|
|
1728
|
+
...tool.description ? { description: tool.description } : {},
|
|
1729
|
+
...tool.inputSchema ? { inputSchema: tool.inputSchema } : {},
|
|
1730
|
+
resourceUri: uiResourceUriFromMeta(meta),
|
|
1731
|
+
visibility: visibilityFromMeta(meta),
|
|
1732
|
+
...cspFromMeta(meta) ? { csp: cspFromMeta(meta) } : {}
|
|
1733
|
+
};
|
|
1734
|
+
});
|
|
1735
|
+
},
|
|
1736
|
+
listResources: () => orEmpty(
|
|
1737
|
+
() => client.listResources(void 0, { timeout: REQUEST_TIMEOUT_MS }),
|
|
1738
|
+
{ resources: [] }
|
|
1739
|
+
),
|
|
1740
|
+
listResourceTemplates: () => orEmpty(
|
|
1741
|
+
() => client.listResourceTemplates(void 0, { timeout: REQUEST_TIMEOUT_MS }),
|
|
1742
|
+
{ resourceTemplates: [] }
|
|
1743
|
+
),
|
|
1744
|
+
listPrompts: () => orEmpty(
|
|
1745
|
+
() => client.listPrompts(void 0, { timeout: REQUEST_TIMEOUT_MS }),
|
|
1746
|
+
{ prompts: [] }
|
|
1747
|
+
),
|
|
1748
|
+
getPrompt(name, args) {
|
|
1749
|
+
return client.getPrompt({ name, arguments: args }, { timeout: REQUEST_TIMEOUT_MS });
|
|
1750
|
+
},
|
|
1751
|
+
async readResource(uri) {
|
|
1752
|
+
const result = await client.readResource({ uri }, { timeout: REQUEST_TIMEOUT_MS });
|
|
1753
|
+
const content = result.contents?.[0];
|
|
1754
|
+
if (!content) return null;
|
|
1755
|
+
const text = "text" in content && typeof content.text === "string" ? content.text : void 0;
|
|
1756
|
+
const blob = "blob" in content && typeof content.blob === "string" ? content.blob : void 0;
|
|
1757
|
+
return {
|
|
1758
|
+
uri: content.uri,
|
|
1759
|
+
...content.mimeType ? { mimeType: content.mimeType } : {},
|
|
1760
|
+
...text != null ? { text } : {},
|
|
1761
|
+
...blob != null ? { blob } : {},
|
|
1762
|
+
...cspFromMeta(content._meta) ? { csp: cspFromMeta(content._meta) } : {}
|
|
1763
|
+
};
|
|
1764
|
+
},
|
|
1765
|
+
async callTool(name, args) {
|
|
1766
|
+
return client.callTool({ name, arguments: args }, void 0, { timeout: REQUEST_TIMEOUT_MS });
|
|
1767
|
+
},
|
|
1768
|
+
capabilities() {
|
|
1769
|
+
const caps = client.getServerCapabilities() ?? {};
|
|
1770
|
+
return {
|
|
1771
|
+
tools: caps.tools ?? {},
|
|
1772
|
+
resources: caps.resources ?? {},
|
|
1773
|
+
prompts: caps.prompts ?? {},
|
|
1774
|
+
completions: caps.completions ?? {},
|
|
1775
|
+
...caps.logging ? { logging: caps.logging } : {}
|
|
1776
|
+
};
|
|
1777
|
+
},
|
|
1778
|
+
forward(method, params) {
|
|
1779
|
+
return client.request(
|
|
1780
|
+
{ method, params },
|
|
1781
|
+
ResultSchema,
|
|
1782
|
+
{ timeout: REQUEST_TIMEOUT_MS }
|
|
1783
|
+
);
|
|
1784
|
+
},
|
|
1785
|
+
async close() {
|
|
1786
|
+
await client.close();
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
function transportForLaunch(launch) {
|
|
1791
|
+
if (launch.kind === "stdio") {
|
|
1792
|
+
return new StdioClientTransport({
|
|
1793
|
+
command: launch.command,
|
|
1794
|
+
args: launch.args,
|
|
1795
|
+
env: { ...getDefaultEnvironment(), ...launch.env },
|
|
1796
|
+
stderr: "ignore",
|
|
1797
|
+
...launch.cwd ? { cwd: launch.cwd } : {}
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
const url = new URL(launch.url);
|
|
1801
|
+
const requestInit = launch.headers ? { headers: launch.headers } : void 0;
|
|
1802
|
+
if (launch.kind === "sse") {
|
|
1803
|
+
return new SSEClientTransport(url, requestInit ? { requestInit } : void 0);
|
|
1804
|
+
}
|
|
1805
|
+
return new StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// src/mcp-ui/host.ts
|
|
1809
|
+
var TOOL_TTL_MS = 5 * 60 * 1e3;
|
|
1810
|
+
var McpUiHost = class {
|
|
1811
|
+
constructor(opts) {
|
|
1812
|
+
this.opts = opts;
|
|
1813
|
+
}
|
|
1814
|
+
opts;
|
|
1815
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1816
|
+
toolLists = /* @__PURE__ */ new Map();
|
|
1817
|
+
proxy;
|
|
1818
|
+
proxyDirs = /* @__PURE__ */ new Map();
|
|
1819
|
+
async viewForClaudeTool(toolName, projectDirs) {
|
|
1820
|
+
const entries = this.userEntries(projectDirs);
|
|
1821
|
+
const match = matchClaudeMcpTool(toolName, Object.keys(entries));
|
|
1822
|
+
if (!match) return null;
|
|
1823
|
+
const launch = parseMcpServerLaunch(entries[match.server] ?? {});
|
|
1824
|
+
if (!launch) return null;
|
|
1825
|
+
try {
|
|
1826
|
+
const session = await this.sessionFor(match.server, launch);
|
|
1827
|
+
const tools = await this.toolsFor(match.server, launch, session);
|
|
1828
|
+
const tool = tools.find((item) => item.name === match.tool);
|
|
1829
|
+
if (!tool?.resourceUri) return null;
|
|
1830
|
+
const resource = await session.readResource(tool.resourceUri);
|
|
1831
|
+
if (!resource) return null;
|
|
1832
|
+
const html = htmlFromResourceContent(resource);
|
|
1833
|
+
if (!html) return null;
|
|
1834
|
+
const csp = resource.csp ?? tool.csp;
|
|
1835
|
+
return {
|
|
1836
|
+
server: match.server,
|
|
1837
|
+
tool: match.tool,
|
|
1838
|
+
uri: html.uri,
|
|
1839
|
+
mimeType: html.mimeType,
|
|
1840
|
+
html: html.html,
|
|
1841
|
+
csp: mcpUiContentSecurityPolicy(csp)
|
|
1842
|
+
};
|
|
1843
|
+
} catch (err) {
|
|
1844
|
+
console.error(
|
|
1845
|
+
`[mcp-ui] ${match.server}/${match.tool}:`,
|
|
1846
|
+
err instanceof Error ? err.message : err
|
|
1847
|
+
);
|
|
1848
|
+
return null;
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Point Claude at loopback HTTP so it shares this host's upstream session
|
|
1853
|
+
* and never sees `visibility: ["app"]` tools.
|
|
1854
|
+
*/
|
|
1855
|
+
async proxyEntries(entries, projectDirs) {
|
|
1856
|
+
const names = Object.keys(entries).filter((name) => name.trim());
|
|
1857
|
+
if (names.length === 0) return {};
|
|
1858
|
+
const dirsKey = dirsKeyFor(projectDirs);
|
|
1859
|
+
this.proxyDirs.set(dirsKey, projectDirs);
|
|
1860
|
+
try {
|
|
1861
|
+
const listen = await this.ensureProxy();
|
|
1862
|
+
const out = {};
|
|
1863
|
+
for (const name of names) {
|
|
1864
|
+
out[name] = {
|
|
1865
|
+
type: "http",
|
|
1866
|
+
url: listen.urlFor(name, dirsKey),
|
|
1867
|
+
headers: listen.headers
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
return out;
|
|
1871
|
+
} catch (err) {
|
|
1872
|
+
console.error("[mcp-ui] proxy:", err instanceof Error ? err.message : err);
|
|
1873
|
+
return { ...entries };
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
async callTool(server, tool, args, projectDirs) {
|
|
1877
|
+
const entry = this.userEntries(projectDirs)[server];
|
|
1878
|
+
if (!entry) throw new Error(`MCP server ${server} is not available`);
|
|
1879
|
+
const launch = parseMcpServerLaunch(entry);
|
|
1880
|
+
if (!launch) throw new Error(`MCP server ${server} is not available`);
|
|
1881
|
+
const session = await this.sessionFor(server, launch);
|
|
1882
|
+
const tools = await this.toolsFor(server, launch, session);
|
|
1883
|
+
const info = tools.find((item) => item.name === tool);
|
|
1884
|
+
if (!info || !toolVisibleTo(info.visibility, "app")) {
|
|
1885
|
+
throw new Error(`Tool ${tool} is not available to the app`);
|
|
1886
|
+
}
|
|
1887
|
+
return session.callTool(tool, args);
|
|
1888
|
+
}
|
|
1889
|
+
async readResource(server, uri, projectDirs) {
|
|
1890
|
+
const session = await this.sessionForName(server, projectDirs);
|
|
1891
|
+
if (!session) throw new Error(`MCP server ${server} is not available`);
|
|
1892
|
+
const resource = await session.readResource(uri);
|
|
1893
|
+
if (!resource) throw new Error(`Resource ${uri} was empty`);
|
|
1894
|
+
return {
|
|
1895
|
+
contents: [
|
|
1896
|
+
{
|
|
1897
|
+
uri: resource.uri,
|
|
1898
|
+
...resource.mimeType ? { mimeType: resource.mimeType } : {},
|
|
1899
|
+
...resource.text != null ? { text: resource.text } : {},
|
|
1900
|
+
...resource.blob != null ? { blob: resource.blob } : {}
|
|
1901
|
+
}
|
|
1902
|
+
]
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
async closeAll() {
|
|
1906
|
+
const pending = [...this.sessions.values()];
|
|
1907
|
+
const proxy = this.proxy;
|
|
1908
|
+
this.sessions.clear();
|
|
1909
|
+
this.toolLists.clear();
|
|
1910
|
+
this.proxyDirs.clear();
|
|
1911
|
+
this.proxy = void 0;
|
|
1912
|
+
await Promise.all([
|
|
1913
|
+
...pending.map((session) => session.then((open) => open.close()).catch(() => void 0)),
|
|
1914
|
+
proxy ? proxy.then((listen) => listen.close()).catch(() => void 0) : Promise.resolve()
|
|
1915
|
+
]);
|
|
1916
|
+
}
|
|
1917
|
+
ensureProxy() {
|
|
1918
|
+
if (this.proxy) return this.proxy;
|
|
1919
|
+
this.proxy = listenMcpUiProxy({
|
|
1920
|
+
sessionFor: (server, dirsKey) => {
|
|
1921
|
+
if (dirsKey && !this.proxyDirs.has(dirsKey)) return Promise.resolve(null);
|
|
1922
|
+
return this.sessionForName(server, this.proxyDirs.get(dirsKey));
|
|
1923
|
+
}
|
|
1924
|
+
}).catch((err) => {
|
|
1925
|
+
this.proxy = void 0;
|
|
1926
|
+
throw err;
|
|
1927
|
+
});
|
|
1928
|
+
return this.proxy;
|
|
1929
|
+
}
|
|
1930
|
+
userEntries(projectDirs) {
|
|
1931
|
+
const out = {};
|
|
1932
|
+
for (const [name, entry] of Object.entries(this.opts.entries(projectDirs))) {
|
|
1933
|
+
if (!name.trim() || isInjectedOrchMcpName(name) || !entry) continue;
|
|
1934
|
+
out[name] = entry;
|
|
1935
|
+
}
|
|
1936
|
+
return out;
|
|
1937
|
+
}
|
|
1938
|
+
async sessionForName(server, projectDirs) {
|
|
1939
|
+
const entry = this.userEntries(projectDirs)[server];
|
|
1940
|
+
if (!entry) return null;
|
|
1941
|
+
const launch = parseMcpServerLaunch(entry);
|
|
1942
|
+
if (!launch) return null;
|
|
1943
|
+
return this.sessionFor(server, launch);
|
|
1944
|
+
}
|
|
1945
|
+
sessionFor(server, launch) {
|
|
1946
|
+
const key = `${server}
|
|
1947
|
+
${JSON.stringify(launch)}`;
|
|
1948
|
+
const existing = this.sessions.get(key);
|
|
1949
|
+
if (existing) return existing;
|
|
1950
|
+
const open = this.opts.openSession ?? openMcpUiSession;
|
|
1951
|
+
const pending = open(launch).catch((err) => {
|
|
1952
|
+
this.sessions.delete(key);
|
|
1953
|
+
throw err;
|
|
1954
|
+
});
|
|
1955
|
+
this.sessions.set(key, pending);
|
|
1956
|
+
return pending;
|
|
1957
|
+
}
|
|
1958
|
+
async toolsFor(server, launch, session) {
|
|
1959
|
+
const key = `${server}
|
|
1960
|
+
${JSON.stringify(launch)}`;
|
|
1961
|
+
const cached = this.toolLists.get(key);
|
|
1962
|
+
if (cached && cached.key === key && Date.now() - cached.at < TOOL_TTL_MS) return cached.tools;
|
|
1963
|
+
const tools = await session.listTools();
|
|
1964
|
+
this.toolLists.set(key, { at: Date.now(), key, tools });
|
|
1965
|
+
return tools;
|
|
1966
|
+
}
|
|
1967
|
+
};
|
|
1968
|
+
var singleton;
|
|
1969
|
+
function getMcpUiHost() {
|
|
1970
|
+
if (!singleton) {
|
|
1971
|
+
singleton = new McpUiHost({
|
|
1972
|
+
entries: (projectDirs) => listUserClaudeMcpServerEntries(homedir4(), projectDirs)
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
return singleton;
|
|
1976
|
+
}
|
|
1977
|
+
async function proxyClaudeUserMcp(orchestrator, projectDirs) {
|
|
1978
|
+
if (orchestrator) return {};
|
|
1979
|
+
const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
|
|
1980
|
+
return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries(homedir4(), dirs), dirs);
|
|
1981
|
+
}
|
|
1982
|
+
function closeMcpUiHost() {
|
|
1983
|
+
const host = singleton;
|
|
1984
|
+
singleton = void 0;
|
|
1985
|
+
if (host) void host.closeAll();
|
|
1986
|
+
}
|
|
1987
|
+
function dirsKeyFor(projectDirs) {
|
|
1988
|
+
if (!projectDirs?.length) return "";
|
|
1989
|
+
return createHash("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1496
1992
|
// src/agents/types.ts
|
|
1497
1993
|
var PLAN_MODE_INSTRUCTION = "Plan mode is active and must remain active until the user turns Plan mode off in the UI (or Approves / Hands off the plan). Analyze the codebase, search and read files as needed, and produce or refine a clear implementation plan. Do not modify, create, or delete any project files except via Sideboard MCP present_plan (writes .context/attachments/plan.md). When you need a clarifying decision (approach forks, auth choice, scope) \u2014 not greetings, check-ins, or an invented task menu: (1) first write a short chat message that explains the decision and what each option means (tradeoffs, when to pick it) \u2014 do not leave the user staring at bare labels; (2) then call Sideboard MCP ask_user with the same options, including a description on every option. If one option is the obvious default, proceed without asking. Sideboard shows questions in the composer and mirrors them in chat. After ask_user, wait for the user's next message with their answers before finalizing the plan. When the plan is ready for approval: (1) call present_plan with the full markdown plan (title + content) so Sideboard saves .context/attachments/plan.md and shows it in chat for Approve / Hand off / Copy; (2) Claude should also call ExitPlanMode after present_plan. Do not skip present_plan \u2014 the plan must be a markdown file, not only chat prose.";
|
|
1498
1994
|
var PLAN_MODE_OFF_INSTRUCTION = "Plan mode is off. Do not call present_plan or ExitPlanMode \u2014 those only work when the user turns Plan on in the composer. If you need to propose an approach, write it in chat.";
|
|
@@ -2063,7 +2559,7 @@ var claudeAdapter = {
|
|
|
2063
2559
|
});
|
|
2064
2560
|
const injectedBrightsyNames = injectedServers.filter((s) => s.name === "brightsy" || s.name.startsWith("brightsy_")).map((s) => s.name);
|
|
2065
2561
|
const chromeOn = claudeChromeEnabled();
|
|
2066
|
-
const userMcpEntries =
|
|
2562
|
+
const userMcpEntries = await proxyClaudeUserMcp(isOrchestrator, [thread.repoPath, thread.worktreePath]);
|
|
2067
2563
|
let allowedTools;
|
|
2068
2564
|
if (isOrchestrator) {
|
|
2069
2565
|
allowedTools = [
|
|
@@ -2401,7 +2897,7 @@ function unwrapCodexMcpResult(result) {
|
|
|
2401
2897
|
return String(result);
|
|
2402
2898
|
}
|
|
2403
2899
|
}
|
|
2404
|
-
function
|
|
2900
|
+
function asRecord3(value) {
|
|
2405
2901
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2406
2902
|
return value;
|
|
2407
2903
|
}
|
|
@@ -2554,7 +3050,7 @@ var codexAdapter = {
|
|
|
2554
3050
|
}
|
|
2555
3051
|
if (itemType === "mcp_tool_call" && item.id && item.server && item.tool) {
|
|
2556
3052
|
const name = `mcp__${item.server}__${item.tool}`;
|
|
2557
|
-
const input =
|
|
3053
|
+
const input = asRecord3(item.arguments);
|
|
2558
3054
|
const events = [
|
|
2559
3055
|
{ type: "tool_use", id: item.id, name, input }
|
|
2560
3056
|
];
|
|
@@ -3039,7 +3535,7 @@ var FALLBACK_OPENCODE_MODELS = [
|
|
|
3039
3535
|
];
|
|
3040
3536
|
var cachedOpencodeModels = null;
|
|
3041
3537
|
var OPENCODE_MODEL_CACHE_MS = 5 * 60 * 1e3;
|
|
3042
|
-
function
|
|
3538
|
+
function asRecord4(value) {
|
|
3043
3539
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3044
3540
|
return value;
|
|
3045
3541
|
}
|
|
@@ -3210,7 +3706,7 @@ var opencodeAdapter = {
|
|
|
3210
3706
|
const name = part?.tool ?? part?.name ?? obj.tool ?? obj.name ?? "tool";
|
|
3211
3707
|
const state = part?.state;
|
|
3212
3708
|
const input = (state?.input && typeof state.input === "object" ? state.input : void 0) ?? part?.input ?? obj.input;
|
|
3213
|
-
const metadata =
|
|
3709
|
+
const metadata = asRecord4(state?.metadata);
|
|
3214
3710
|
const id = opencodeToolId(
|
|
3215
3711
|
name,
|
|
3216
3712
|
part?.callID ?? part?.id ?? obj.id,
|
|
@@ -3245,7 +3741,7 @@ var opencodeAdapter = {
|
|
|
3245
3741
|
}
|
|
3246
3742
|
if (obj.type === "subtask_event") {
|
|
3247
3743
|
const parentId = str(obj.childSessionID) ?? str(obj.childSessionId);
|
|
3248
|
-
const part =
|
|
3744
|
+
const part = asRecord4(obj.part);
|
|
3249
3745
|
if (!parentId || !part) return null;
|
|
3250
3746
|
if (part.type === "text" && str(part.text)) {
|
|
3251
3747
|
return withTaskParent(
|
|
@@ -3256,8 +3752,8 @@ var opencodeAdapter = {
|
|
|
3256
3752
|
if (part.type === "tool" || str(part.tool)) {
|
|
3257
3753
|
const name = str(part.tool) ?? str(part.name) ?? "tool";
|
|
3258
3754
|
const id = str(part.callID) ?? str(part.id) ?? `${parentId}-${name}`;
|
|
3259
|
-
const state =
|
|
3260
|
-
const input =
|
|
3755
|
+
const state = asRecord4(part.state);
|
|
3756
|
+
const input = asRecord4(state?.input) ?? asRecord4(part.input);
|
|
3261
3757
|
const nested = [
|
|
3262
3758
|
withEventParentId({ type: "tool_use", id, name, input }, parentId)
|
|
3263
3759
|
];
|
|
@@ -3976,11 +4472,6 @@ export {
|
|
|
3976
4472
|
adjustBrightsyTurnExit,
|
|
3977
4473
|
listBrightsyChatTargets,
|
|
3978
4474
|
brightsyAdapter,
|
|
3979
|
-
matchClaudeMcpTool,
|
|
3980
|
-
parseMcpServerLaunch,
|
|
3981
|
-
uiResourceUriFromMeta,
|
|
3982
|
-
cspFromMeta,
|
|
3983
|
-
htmlFromResourceContent,
|
|
3984
4475
|
mcpUiContentSecurityPolicy,
|
|
3985
4476
|
SIDEBOARD_MCP_PROFILE_ENV,
|
|
3986
4477
|
SIDEBOARD_THREAD_ID_ENV,
|
|
@@ -4005,7 +4496,8 @@ export {
|
|
|
4005
4496
|
registerPackagedUserMcpClients,
|
|
4006
4497
|
isInjectedOrchMcpName,
|
|
4007
4498
|
userMcpNamesToDisable,
|
|
4008
|
-
|
|
4499
|
+
getMcpUiHost,
|
|
4500
|
+
closeMcpUiHost,
|
|
4009
4501
|
PLAN_MODE_INSTRUCTION,
|
|
4010
4502
|
PLAN_MODE_OFF_INSTRUCTION,
|
|
4011
4503
|
planModeTurnInstruction,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
parseSessionQuotaResetAt,
|
|
28
28
|
planModeTurnInstruction,
|
|
29
29
|
resolveQuotaFallbackAgent
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-2P2HYXA2.js";
|
|
31
31
|
import {
|
|
32
32
|
REVIEW_TAB_TITLE,
|
|
33
33
|
applyWorktreePkgCacheEnv,
|
|
@@ -1500,6 +1500,15 @@ function thisProcessShouldDrainAgentQueues() {
|
|
|
1500
1500
|
return !isDesktopHostAlive();
|
|
1501
1501
|
}
|
|
1502
1502
|
|
|
1503
|
+
// src/mcp-ui/model-context.ts
|
|
1504
|
+
function takeMcpAppModelContext(threadId) {
|
|
1505
|
+
const thread = readThread(threadId);
|
|
1506
|
+
const text3 = thread?.mcpAppModelContext?.trim();
|
|
1507
|
+
if (!text3) return void 0;
|
|
1508
|
+
updateThread(threadId, { mcpAppModelContext: null });
|
|
1509
|
+
return text3;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1503
1512
|
// src/agents/tool-permission.ts
|
|
1504
1513
|
import { randomUUID } from "crypto";
|
|
1505
1514
|
import { dirname } from "path";
|
|
@@ -6429,7 +6438,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6429
6438
|
${prompt}`;
|
|
6430
6439
|
}
|
|
6431
6440
|
async function defaultDeps() {
|
|
6432
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6441
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-S44BMNN7.js");
|
|
6433
6442
|
const orch = getOrchestrator2();
|
|
6434
6443
|
return {
|
|
6435
6444
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -7516,6 +7525,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
7516
7525
|
orchestrationReminder,
|
|
7517
7526
|
prGateDirective,
|
|
7518
7527
|
injectedNoticeContext,
|
|
7528
|
+
takeMcpAppModelContext(threadId),
|
|
7519
7529
|
expandedPrompt
|
|
7520
7530
|
].filter(Boolean).join("\n\n");
|
|
7521
7531
|
if (!autoContinue && sentAttachments) {
|