Simplify agent list UI and add bearer token auth for Codex MCP
- Replace SectionList with FlatList in agent list, sorting attention items to top - Persist server selection preference in create agent modal - Add bearer token authentication support for agent-control MCP endpoint - Pass bearer token through Codex wrapper script for MCP auth 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
bf0ec80fb5
commit
6a9b57ae0c
@@ -277,9 +277,29 @@ function resolveAgentControlMcpUrl(config: CodexAgentConfig): string | null {
|
||||
: baseUrl;
|
||||
}
|
||||
|
||||
function resolveAgentControlBearerToken(
|
||||
config: CodexAgentConfig
|
||||
): string | null {
|
||||
const header = config.agentControlMcp?.headers?.Authorization;
|
||||
if (!header || typeof header !== "string") {
|
||||
return null;
|
||||
}
|
||||
const trimmed = header.trim();
|
||||
if (trimmed.toLowerCase().startsWith("bearer ")) {
|
||||
const token = trimmed.slice("bearer ".length).trim();
|
||||
return token.length > 0 ? token : null;
|
||||
}
|
||||
if (trimmed.toLowerCase().startsWith("basic ")) {
|
||||
const token = trimmed.slice("basic ".length).trim();
|
||||
return token.length > 0 ? token : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function createCodexWrapperScript(
|
||||
realCodexPath: string,
|
||||
mcpUrl: string,
|
||||
mcpBearerToken: string | null,
|
||||
developerInstructions: string
|
||||
): Promise<CodexWrapperInfo> {
|
||||
const templatePath = await resolveCodexWrapperTemplatePath();
|
||||
@@ -289,6 +309,10 @@ async function createCodexWrapperScript(
|
||||
const content = template
|
||||
.replace("__REAL_CODEX__", escapeBashValue(realCodexPath))
|
||||
.replace("__MCP_URL__", escapeBashValue(mcpUrl))
|
||||
.replace(
|
||||
"__MCP_BEARER__",
|
||||
escapeBashValue(mcpBearerToken ?? "")
|
||||
)
|
||||
.replace(
|
||||
"__DEV_INSTRUCTIONS__",
|
||||
escapeBashTomlValue(encodeTomlBasicString(developerInstructions))
|
||||
@@ -459,6 +483,7 @@ export class CodexAgentClient implements AgentClient {
|
||||
config: CodexAgentConfig
|
||||
): Promise<CodexSessionBootstrap> {
|
||||
const mcpUrl = resolveAgentControlMcpUrl(config);
|
||||
const mcpBearerToken = resolveAgentControlBearerToken(config);
|
||||
const developerInstructions = resolveDeveloperInstructions(config);
|
||||
if ((!mcpUrl && !developerInstructions) || !this.baseCodexPath) {
|
||||
if ((mcpUrl || developerInstructions) && !this.baseCodexPath) {
|
||||
@@ -470,6 +495,7 @@ export class CodexAgentClient implements AgentClient {
|
||||
const wrapper = await createCodexWrapperScript(
|
||||
this.baseCodexPath,
|
||||
mcpUrl ?? "",
|
||||
mcpBearerToken,
|
||||
developerInstructions ?? ""
|
||||
);
|
||||
const env = {
|
||||
|
||||
@@ -28,17 +28,34 @@ const BASIC_AUTH_USERS = { mo: "bo" } as const;
|
||||
function createServer() {
|
||||
const app = express();
|
||||
|
||||
const [agentMcpUser, agentMcpPassword] =
|
||||
Object.entries(BASIC_AUTH_USERS)[0] ?? [];
|
||||
const agentMcpBearerToken =
|
||||
agentMcpUser && agentMcpPassword
|
||||
? Buffer.from(`${agentMcpUser}:${agentMcpPassword}`).toString("base64")
|
||||
: undefined;
|
||||
|
||||
// Serve static files from public directory (no auth required for APK downloads)
|
||||
app.use("/public", express.static("public"));
|
||||
|
||||
// Basic authentication (skip for /public routes)
|
||||
app.use(
|
||||
basicAuth({
|
||||
users: BASIC_AUTH_USERS,
|
||||
challenge: true,
|
||||
realm: "Voice Assistant",
|
||||
})
|
||||
);
|
||||
const basicAuthMiddleware = basicAuth({
|
||||
users: BASIC_AUTH_USERS,
|
||||
challenge: true,
|
||||
realm: "Voice Assistant",
|
||||
});
|
||||
app.use((req, res, next) => {
|
||||
if (agentMcpBearerToken && req.path.startsWith("/mcp/agents")) {
|
||||
const authHeader = req.header("authorization") ?? "";
|
||||
if (authHeader.startsWith("Bearer ")) {
|
||||
const token = authHeader.slice("Bearer ".length).trim();
|
||||
if (token === agentMcpBearerToken) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
}
|
||||
return basicAuthMiddleware(req, res, next);
|
||||
});
|
||||
|
||||
// Middleware
|
||||
app.use(express.json());
|
||||
@@ -155,6 +172,15 @@ async function main() {
|
||||
req,
|
||||
res
|
||||
) => {
|
||||
if (process.env.MCP_DEBUG === "1") {
|
||||
console.log("[Agent MCP] request", {
|
||||
method: req.method,
|
||||
url: req.originalUrl,
|
||||
sessionId: req.header("mcp-session-id"),
|
||||
authorization: req.header("authorization"),
|
||||
body: req.body,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const sessionId = req.header("mcp-session-id");
|
||||
let transport = sessionId
|
||||
|
||||
Reference in New Issue
Block a user