I would like to pass a client secret/API key to a remote MCP server inside the Authorization header. I know that remote MCPs are meant to be used with OAuth, but in my case I'd rather just use the existing API keys.
mcp remote
package allows to do so
"mcpServers": {
"remote-example": {
"command": "npx",
"args": [
"mcp-remote",
"https://remote.mcp.server/sse",
"--header",
"Authorization: Bearer ${AUTH_TOKEN}"
]
}
}
}
I'm using npm create cloudflare@latest -- my-mcp-server --template=cloudflare/ai/demos/remote-mcp-authless
template to set up a Remote MCP without Oauth. Now, all I want is to pass a token from the headers inside the tool call, so it's available inside the extra
variable. Is there a way to do it?
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
// Define our MCP agent with tools
export class MyMCP extends McpAgent {
server = new McpServer({
name: "Authless Calculator",
version: "1.0.0",
});
async init() {
// Simple addition tool
this.server.tool(
"add",
{ a: z.number(), b: z.number() },
async ({ a, b }, extra) => {
// I want API key/token to be available here
console.log({ extra });
return {
content: [{ type: "text", text: String(a + b) }],
};
}
);
// Calculator tool with multiple operations
this.server.tool(
"calculate",
{
operation: z.enum(["add", "subtract", "multiply", "divide"]),
a: z.number(),
b: z.number(),
},
async ({ operation, a, b }) => {
let result: number;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b === 0)
return {
content: [
{
type: "text",
text: "Error: Cannot divide by zero",
},
],
};
result = a / b;
break;
}
return { content: [{ type: "text", text: String(result) }] };
}
);
}
}
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
const authHeader = request.headers.get("authorization");
if (!authHeader?.startsWith("Bearer ")) {
return new Response("Forbidden", { status: 403 });
}
console.log({authHeader})
const token = authHeader.split(/\s+/)[1] ?? "";
// I can get a token from the headers
// But how do I pass it to the tools?
console.log({ token });
if (url.pathname === "/sse" || url.pathname === "/sse/message") {
// @ts-ignore
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
}
if (url.pathname === "/mcp") {
// @ts-ignore
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
return new Response("Not found", { status: 404 });
},
};
You can set ctx.props.XYZ_FIELD
in your outer fetch
function and then access that as this.props.XYZ_FIELD
within your server tool handler.
So with your example that would become something like:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
// Define our MCP agent with tools
export class MyMCP extends McpAgent {
server = new McpServer({
name: "Authless Calculator",
version: "1.0.0",
});
async init() {
// Simple addition tool
this.server.tool(
"add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => {
token = this.props.myToken as string; // <-- this is how you can access it
return {
content: [{ type: "text", text: String(a + b) }],
};
}
);
// ...
}
}
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
const authHeader = request.headers.get("authorization");
if (!authHeader?.startsWith("Bearer ")) {
return new Response("Forbidden", { status: 403 });
}
console.log({authHeader})
const token = authHeader.split(/\s+/)[1] ?? "";
ctx.props.myToken = token; // <-- this is how you can set it
if (url.pathname === "/sse" || url.pathname === "/sse/message") {
// @ts-ignore
return MyMCP.serveSSE("/sse").fetch(request, env, ctx);
}
if (url.pathname === "/mcp") {
// @ts-ignore
return MyMCP.serve("/mcp").fetch(request, env, ctx);
}
return new Response("Not found", { status: 404 });
},
};