Files
open-multi-agent/examples/integrations/mcp-github.ts
T
Jack Chen 6b6330208c fix: redact secrets in traces, bash env, and dashboard payloads (#263)
* fix: redact secrets in traces, bash env, and dashboard payloads

Adds src/utils/redaction.ts and applies best-effort redaction at three
sinks where sensitive data could leak:
- tool_call trace events (input, output)
- the bash tool's output buffer
- the post-run dashboard's embedded JSON payload

The bash tool no longer inherits the parent process.env. Child processes
receive a fixed allowlist (HOME, LANG, LC_ALL, LOGNAME, PATH, SHELL,
TEMP, TERM, TMP, TMPDIR, USER), so provider API keys and similar
secrets are no longer visible to spawned shells.

The post-run dashboard no longer loads Tailwind CDN, Google Fonts, or
Material Symbols at view time. The page is self-contained and renders
without network access.

llmMessagesToAiSdkModelMessages no longer concatenates the opaque
redactedData payload into the reasoning text emitted to the AI SDK; it
emits only the [redacted_thinking] marker.

The MCP GitHub example uses --no-install instead of -y and passes an
explicit env allowlist instead of spreading process.env.

* fix(redaction): consume full value for quoted and unquoted assignments

The single ASSIGNMENT_PATTERN stopped at the first whitespace inside a
value, so values with spaces were only partially redacted and the
remainder leaked into trace events, dashboard payloads, and the bash
output buffer. Three reviewer-reported cases on PR #263:

  password=secret value     -> password=[redacted] value
  password="secret value"   -> password="[redacted]" value"
  apiKey: "abc def"         -> apiKey: "[redacted]" def"

Splits the single pattern into two passes. The quoted pass consumes
to the back-referenced closing quote so password="my pass" is fully
matched. The unquoted pass consumes to the next structural delimiter
(comma, semicolon, brace, bracket, newline) so password=my pass is
fully matched.

Adds tests/redaction.test.ts covering the three reviewer cases, the
existing Authorization orphan-bracket regression, common token literal
patterns, and idempotence.
2026-05-28 00:53:34 +08:00

63 lines
1.6 KiB
TypeScript

/**
* MCP GitHub Tools
*
* Connect an MCP server over stdio and register all exposed MCP tools as
* standard open-multi-agent tools.
*
* Run:
* npx tsx examples/integrations/mcp-github.ts
*
* Prerequisites:
* - GEMINI_API_KEY
* - GITHUB_TOKEN
* - @modelcontextprotocol/sdk installed
* - @modelcontextprotocol/server-github installed locally
*/
import { Agent, ToolExecutor, ToolRegistry, registerBuiltInTools } from '../../src/index.js'
import { connectMCPTools } from '../../src/mcp.js'
if (!process.env.GITHUB_TOKEN?.trim()) {
console.error('Missing GITHUB_TOKEN: set a GitHub personal access token in the environment.')
process.exit(1)
}
const { tools, disconnect } = await connectMCPTools({
command: 'npx',
args: ['--no-install', '@modelcontextprotocol/server-github'],
env: {
GITHUB_TOKEN: process.env.GITHUB_TOKEN,
HOME: process.env.HOME,
PATH: process.env.PATH,
TMPDIR: process.env.TMPDIR,
},
namePrefix: 'github',
})
const registry = new ToolRegistry()
registerBuiltInTools(registry)
for (const tool of tools) registry.register(tool)
const executor = new ToolExecutor(registry)
const agent = new Agent(
{
name: 'github-agent',
model: 'gemini-2.5-flash',
provider: 'gemini',
tools: tools.map((tool) => tool.name),
systemPrompt: 'Use GitHub MCP tools to answer repository questions.',
},
registry,
executor,
)
try {
const result = await agent.run(
'List the last 3 open issues in open-multi-agent/open-multi-agent with title and number.',
)
console.log(result.output)
} finally {
await disconnect()
}