Skip to content

Commit 5b10c29

Browse files
committed
Add custom header to mcp
1 parent e501536 commit 5b10c29

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Context } from "../util/context"
2+
3+
interface McpCallContext {
4+
metadata?: Record<string, string>
5+
}
6+
7+
const context = Context.create<McpCallContext>("mcp-call")
8+
9+
export const McpCallContext = {
10+
provide<R>(value: McpCallContext, fn: () => R): R {
11+
return context.provide(value, fn)
12+
},
13+
14+
get current(): McpCallContext | undefined {
15+
try {
16+
return context.use()
17+
} catch {
18+
return undefined
19+
}
20+
},
21+
}

packages/opencode/src/mcp/index.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ import { BusEvent } from "../bus/bus-event"
2323
import { Bus } from "@/bus"
2424
import { TuiEvent } from "@/cli/cmd/tui/event"
2525
import open from "open"
26+
import { McpCallContext } from "./context"
27+
28+
function createContextAwareFetch(baseFetch?: typeof globalThis.fetch): typeof globalThis.fetch {
29+
const fetchFn = baseFetch ?? globalThis.fetch
30+
return (input, init) => {
31+
const ctx = McpCallContext.current
32+
if (ctx) {
33+
const headers = new Headers(init?.headers)
34+
if (ctx.metadata) {
35+
for (const [key, value] of Object.entries(ctx.metadata)) {
36+
headers.set(key, value)
37+
}
38+
}
39+
return fetchFn(input, { ...init, headers })
40+
}
41+
return fetchFn(input, init)
42+
}
43+
}
2644

2745
export namespace MCP {
2846
const log = Log.create({ service: "mcp" })
@@ -362,19 +380,22 @@ export namespace MCP {
362380
)
363381
}
364382

383+
const contextFetch = createContextAwareFetch()
365384
const transports: Array<{ name: string; transport: TransportWithAuth }> = [
366385
{
367386
name: "StreamableHTTP",
368387
transport: new StreamableHTTPClientTransport(new URL(mcp.url), {
369388
authProvider,
370389
requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
390+
fetch: contextFetch,
371391
}),
372392
},
373393
{
374394
name: "SSE",
375395
transport: new SSEClientTransport(new URL(mcp.url), {
376396
authProvider,
377397
requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
398+
fetch: contextFetch,
378399
}),
379400
},
380401
]

packages/opencode/src/session/prompt.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import MAX_STEPS from "../session/prompt/max-steps.txt"
2525
import { defer } from "../util/defer"
2626
import { ToolRegistry } from "../tool/registry"
2727
import { MCP } from "../mcp"
28+
import { McpCallContext } from "../mcp/context"
2829
import { LSP } from "../lsp"
2930
import { ReadTool } from "../tool/read"
3031
import { FileTime } from "../file/time"
@@ -111,6 +112,7 @@ export namespace SessionPrompt {
111112
format: MessageV2.Format.optional(),
112113
system: z.string().optional(),
113114
variant: z.string().optional(),
115+
metadata: z.record(z.string(), z.string()).optional(),
114116
parts: z.array(
115117
z.discriminatedUnion("type", [
116118
MessageV2.TextPart.omit({
@@ -184,7 +186,7 @@ export namespace SessionPrompt {
184186
return message
185187
}
186188

187-
return loop({ sessionID: input.sessionID })
189+
return loop({ sessionID: input.sessionID, metadata: input.metadata })
188190
})
189191

190192
export async function resolvePromptParts(template: string): Promise<PromptInput["parts"]> {
@@ -273,6 +275,7 @@ export namespace SessionPrompt {
273275
export const LoopInput = z.object({
274276
sessionID: SessionID.zod,
275277
resume_existing: z.boolean().optional(),
278+
metadata: z.record(z.string(), z.string()).optional(),
276279
})
277280
export const loop = fn(LoopInput, async (input) => {
278281
const { sessionID, resume_existing } = input
@@ -611,6 +614,7 @@ export namespace SessionPrompt {
611614
processor,
612615
bypassAgentCheck,
613616
messages: msgs,
617+
metadata: input.metadata,
614618
})
615619

616620
// Inject StructuredOutput tool if JSON schema mode enabled
@@ -749,6 +753,7 @@ export namespace SessionPrompt {
749753
processor: SessionProcessor.Info
750754
bypassAgentCheck: boolean
751755
messages: MessageV2.WithParts[]
756+
metadata?: Record<string, string>
752757
}) {
753758
using _ = log.time("resolveTools")
754759
const tools: Record<string, AITool> = {}
@@ -864,7 +869,12 @@ export namespace SessionPrompt {
864869
always: ["*"],
865870
})
866871

867-
const result = await execute(args, opts)
872+
const result = await McpCallContext.provide(
873+
{
874+
metadata: input.metadata,
875+
},
876+
() => execute(args, opts),
877+
)
868878

869879
await Plugin.trigger(
870880
"tool.execute.after",

0 commit comments

Comments
 (0)