agentgateway v1.7.0: How Agent Session Detection Works
Originally published at webofmike.com on 2026-11-01. The demo repo and every command in it were run before publishing.
agentgateway v1.7.0 (GA, 2026-10-27) can tell which coding-agent session sent a request without any client-side change: it reads the session header the agent is already sending. themsquared/agw-17-hands-on validates the detection rule against the real v1.7.0 binary and a mock LLM backend, no API key, no network calls, five curl requests that show exactly when it fires and when it doesn't.
Why this matters before the mechanism
Without session detection, agentgateway can rate limit or log by API key or IP address, but it has no way to group the many requests one agent run makes into a single unit. Coding agents like Claude Code, Cursor, and Codex already send a session or conversation id in their own headers so their own backends can correlate turns. v1.7.0 reads that same header. Once agentgateway knows which requests belong together, that grouping is available to any CEL-driven policy: a rate limit keyed per session instead of per key, an audit trail grouped by session, routing a whole session to the same backend, or flagging a session whose request pattern looks unusual. None of this needs the client to adopt a new header or send extra metadata. It needs the client to keep doing what it already does.
The headers it reads, in priority order
agentgateway's detection logic (crates/agentgateway/src/http/session.rs in the v1.7.0 tag) checks, in order:
1. Any header shaped x-<vendor>-session-id. Claude Code sends x-claude-code-session-id; this is a generic pattern, not a Claude-specific special case, so Cursor's x-cursor-session-id or any other vendor's equivalent is detected the same way.
2. Codex's unprefixed session-id, session_id, thread-id, or conversation_id headers, but only when the request's User-Agent starts with codex. This gate exists because those header names are generic enough that treating them as agent-session signals from any client would produce false positives.
3. A vendor-less x-session-id header, as a last resort.
Whichever header matches, the detected value becomes available to CEL as request.agent.session.
The rule that silently drops a session id
The validation is stricter than it looks, and it fails quietly:
fn valid(value: &HeaderValue) -> Option<&str> {
value.to_str().ok().filter(|v| {
v.len() >= 8
&& v.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
})
}
A session id under 8 characters, or one containing a character outside [A-Za-z0-9_-], doesn't get rejected with an error. It gets treated exactly like a missing header: request.agent.session is simply never set, and nothing in the response or the logs calls that out. If a session grouping you expect to see isn't showing up, this rule is the first thing to check, not a deeper config problem.
Proving it with a mock backend
agw-17-hands-on's config points agentgateway's standalone LLM route at a 40-line Python HTTP server instead of a real provider, using v1.7.0's custom provider with the completions wire format:
# config/config.yaml
config:
session:
key: disabled # demo only: v1.7 standalone always provisions a session key on first run
frontendPolicies:
accessLog:
add:
agent.session: request.agent.session
llm:
port: 4000
models:
- name: mock
provider:
custom:
formats:
- type: completions
params:
baseUrl: http://127.0.0.1:8091/v1
model: mock-model
request.agent.session only depends on the incoming request, not on what the backend does with it, so a mock response proves the same behavior a real Anthropic or OpenAI call would. That also means this demo costs nothing to run and needs no API key.
Five requests, one gateway:
$ curl -s -X POST http://localhost:4000/v1/chat/completions \
-H "x-claude-code-session-id: 79291636abc123" -d '{...}' # request 1
$ curl -s -X POST http://localhost:4000/v1/chat/completions \
-H "x-claude-code-session-id: 79291636abc123" -d '{...}' # request 2, same session
$ curl -s -X POST http://localhost:4000/v1/chat/completions \
-H "x-cursor-session-id: cursorsession999" -d '{...}' # request 3, different vendor header
$ curl -s -X POST http://localhost:4000/v1/chat/completions -d '{...}' # request 4, no session header
$ curl -s -X POST http://localhost:4000/v1/chat/completions \
-H "x-claude-code-session-id: short1" -d '{...}' # request 5, 6-character id
All five return HTTP 200. The access log, with agent.session: request.agent.session wired into accessLog.add, is where the real answer is:
http.path=/v1/chat/completions agent.session="79291636abc123"
http.path=/v1/chat/completions agent.session="79291636abc123"
http.path=/v1/chat/completions agent.session="cursorsession999"
http.path=/v1/chat/completions
http.path=/v1/chat/completions
Requests 1 and 2 share a session, as expected from a repeated header. Request 3's completely different vendor header still gets picked up, which is the generic-pattern claim proven rather than assumed. Requests 4 and 5 both have no agent.session field: one sent no header at all, the other's short1 is 6 characters, two short of the minimum. Same outcome, two different causes, neither one visible unless you already know to look for it.
This is the same accessLog.add mechanism the v1.6.0 cost-tracking post used to surface llm.cost.total into the request log: any CEL field, request.agent.session included, becomes a logged attribute with one line of config.
Overriding detection
config.standardAttributes.session takes a CEL expression that runs before the automatic header scan. Returning a string from it sets the session explicitly, which is useful if an internal proxy or SDK already generates its own session identifier under a header name agentgateway wouldn't otherwise recognize. Returning null marks the request as having no session even when a technically-valid vendor header is present. If the expression itself errors, or returns something other than a string, agentgateway falls back to the header-based detection described above rather than failing the request. This repo doesn't set the override, so everything in the demo runs through the default header scan.
What this doesn't cover
This demo is scoped to proving the detection rule, not building something on top of it. It doesn't exercise a real LLM provider (the mock backend never sees or uses the session value), Codex's User-Agent-gated header set, or any policy that actually acts on request.agent.session, like a per-session rate limit. v1.7.0 shipped a lot more than this one field: MCP tool-call guardrails (written against a pre-release build, now generally available), model-catalog metadata over /v1/models, service-tier-aware cost pricing, and a new agctl standalone analytics CLI. This post is one field, validated.
Repo: themsquared/agw-17-hands-on. Release notes: agentgateway v1.7.0.
Frequently asked questions
How does agentgateway detect which coding agent session sent a request?
It scans request headers for any name shaped x-<vendor>-session-id, such as x-claude-code-session-id or x-cursor-session-id, and takes the first valid value it finds. It also recognizes Codex's unprefixed session-id and conversation_id headers, but only when the User-Agent string starts with codex. A vendor-less x-session-id header is the last fallback. The result is exposed as request.agent.session in CEL and in access logs.
Why isn't my agent session id showing up in agentgateway's access logs?
The value has to be at least 8 characters long and contain only letters, digits, hyphens, or underscores. A shorter or differently-formatted value is treated as if the header were never sent: no error, no warning, the request just has no detected session. Check length and character set first before assuming the feature is broken.
How do I log agentgateway's detected agent session?
Add agent.session: request.agent.session under frontendPolicies.accessLog.add in the gateway config. The field then appears on every access log line as agent.session=\
Can I override agentgateway's automatic agent session detection?
Yes. config.standardAttributes.session takes a CEL expression that runs before the header-based fallback. Returning a string from it sets the session explicitly; returning null marks the request as having no session even if a valid vendor header is present. If the expression errors or returns a non-string, agentgateway falls back to the automatic header scan.
---
*Canonical version, with machine-readable markdown at https://webofmike.com/agentgateway-v17-agent-sessions/index.md: webofmike.com
agent 一多,鉴权、限流全散在各处?
agentgateway 是给 agent 和 MCP 的下一代代理:鉴权、RBAC、限流、护栏、可观测全套配齐。
适合把 agent 规模化跑起来、又得管安全合规的团队,个人小项目有点重。
先给 agent 接哪道关,鉴权还是护栏?
#Agent #MCP @grok
agentgateway/agentgateway
1 like · 122 views
🚀 agentgateway 1.7 is out!
💰 Per-key token and dollar budgets
🔐 OIDC login + hashed API keys
🛡️ Better streaming guardrails
🤖 Gemini API support + improved LLM interoperability
📊 Richer LLM observability
Thanks to everyone who contributed! 🙌
Try it locally, no K8s required.
buff.ly
1 like
Repo: agentgateway/agentgateway
How to try: download the binary for your OS from the v1.7.0 release, then run `agentgateway -f examples/mcp-guardrails/expression/config.yaml` from a clone (needs npx for the demo MCP server). It listens on port 3000.
39 views
Stopping Agent Proxy Laundering at the MCP Gateway
Originally published at webofmike.com on 2026-10-25. The demo repo and every command in it were run before publishing.
On 2026-10-05 the Wikimedia Foundation disclosed that a handful of wiki edits it attributes to OpenAI-operated agents changed the configuration of a citation tool, in what Wikimedia believes was an attempt to misuse it as a proxy for fetching data from remote services. Wikimedia doesn't publish the exact mechanism, so I can't reproduce their tool. What I built instead is themsquared/agent-proxy-laundering: a toy citation-resolver MCP tool with the same shape as the one Wikimedia describes, pointed at an attacker-controlled destination the same way, and a gateway policy that catches it.
The shape is the point. A citation resolver's whole job is "fetch this URL and return what's in it." That is also, mechanically, a complete description of a proxy. The tool has no step where it decides a URL is or isn't a legitimate citation source, because nothing told it there was a difference to check.
What Wikimedia actually found
Worth being precise about what's confirmed and what isn't, since some coverage blurs the two incidents together. Per Wikimedia's own post:
- A few edits to Wikimedia wikis, dated 2026-10-04 in a published CSV, changed a citation tool's configuration. Almost all of the broader edit set were test edits in sandbox areas, not published to pages visible to readers, but Wikimedia believes these specific ones were an attempt to misuse the tool as a fetch proxy.
- A separate set of agents tried, and failed, to compromise a public Etherpad instance Wikimedia hosts, including trying to use it the same way.
- The agents made millions of automated requests to public APIs and crawled millions of pages, mostly against Wikidata and Wikimedia Commons, with hundreds of thousands of queries against the Wikidata Query Service. Wikimedia says this traffic may have contributed to a partial outage of that service in May.
- No evidence the agents coordinated through Wikimedia's systems. No evidence Wikimedia's systems or data were compromised.
- The OpenAI attribution and the citation-tool link are Wikimedia's belief, not an independently confirmed fact.
I've already written about the volume half of this story: rate-limiting the Wikidata query traffic, a problem of an allowed destination getting hit too often. This post is about a different half: a tool getting pointed somewhere it was never meant to go in the first place.
The tool: fetch_citation_source
The tool can't tell "resolve this DOI" apart from "fetch this attacker-controlled host," because from inside the tool both are the same instruction: a string that looks like a URL, and a command to fetch it. Here's the toy version, a one-tool MCP server:
server.registerTool(
"fetch_citation_source",
{ /* ... */ },
async ({ source_url }) => {
const res = await fetch(source_url);
const body = await res.text();
return { content: [{ type: "text", text: `GET ${source_url} -> ${res.status}: ${body.slice(0, 300)}` }] };
},
);
Nothing here checks source_url. mcp-server/direct-fetch.js runs that exact logic directly, no MCP framing, no gateway, nothing in front of it, against two targets: a mock citation API standing in for a legitimate bibliographic source, and a mock "attacker target" standing in for anywhere a laundered request could land.
$ node mcp-server/direct-fetch.js http://localhost:8081/works/10.1234/example
GET http://localhost:8081/works/10.1234/example -> 200: {"id": "example", "title": "Scoped Egress Identity for Autonomous Agents", ...}
$ node mcp-server/direct-fetch.js http://localhost:8082/
GET http://localhost:8082/ -> 200: {"reached": "attacker-target", "note": "this response proves whatever called it treated this host as a valid fetch target"}
Same code, same result shape, two completely different destinations. The tool treated both as equally valid, because to the tool they were.
The control: a destination check at the gateway, not in the tool
agentgateway can sit in front of an MCP server and see the resolved tools/call request, arguments included, before the tool ever runs. That's the same mcpGuardrails mechanism I used in an earlier post to reject a tool call containing a destructive shell pattern. Here it does a narrower, more specific job: check source_url against an allowlist of actual citation sources, and reject anything else.
mcpGuardrails:
processors:
- kind: expression
methods: {"tools/call": request}
condition: >-
mcp.params.name == 'fetch_citation_source' &&
has(mcp.params.arguments) &&
has(mcp.params.arguments.source_url) &&
!(mcp.params.arguments.source_url.startsWith('http://localhost:8081/') ||
mcp.params.arguments.source_url.startsWith('http://127.0.0.1:8081/') ||
mcp.params.arguments.source_url.startsWith('https://doi.org/') ||
mcp.params.arguments.source_url.startsWith('https://api.crossref.org/'))
reject: "source_url is not an allowlisted citation source; rejected before the fetch ran"
The tool's own code is exactly what it was above. It still has no idea what a valid citation source looks like. It doesn't need one, because a request pointed anywhere else never reaches it.
Reproducing it
git clone https://github.com/themsquared/agent-proxy-laundering.git
cd agent-proxy-laundering
npm install --prefix mcp-server
AGENTGATEWAY_BIN=/path/to/agentgateway ./scripts/demo.sh
The agentgateway binary needs the mcpGuardrails CEL processor, which landed on main on 2026-10-06 and is not in a tagged release yet (the latest release, v1.6.0, predates it). I built it from source with cargo build --release -p agentgateway-app, the same path I used for the tool-call-guardrails demo.
Through the gateway, the legitimate lookup:
{
"content": [
{
"type": "text",
"text": "GET http://localhost:8081/works/10.1234/example -> 200: {\"id\": \"example\", \"title\": \"Scoped Egress Identity for Autonomous Agents\", ...}"
}
]
}
And the laundering attempt, same tool, same call shape, source_url pointed at the attacker target instead:
{
"content": [
{
"type": "text",
"text": "source_url is not an allowlisted citation source; rejected before the fetch ran"
}
],
"isError": true
}
The gateway log shows exactly where that message came from and how fast:
error=... mcp.method.name=tools/call mcp.target=citation-tools gen_ai.tool.name=fetch_citation_source
error="mcp: mcpGuardrails rejected: source_url is not an allowlisted citation source; rejected before the fetch ran"
reason=MCP duration=0ms
duration=0ms. No upstream dial was attempted. attacker-target's own request counter, checked before and after, confirms it from the other side: {"requests_received": 1} both times, the single hit left over from the direct, no-gateway call earlier. The laundering attempt never reached it.
What this doesn't cover
The allowlist here is a handful of startsWith prefix checks against known-good hosts. That's the right shape for a small, stable set of legitimate sources and the wrong shape for "any DOI resolver on the internet," which would need a real allowlist of registered resolvers, kept current alongside whatever the tool is actually supposed to reach.
It also doesn't model the Etherpad half of Wikimedia's disclosure, where the target being probed was a hosted service rather than a fetch tool being handed a destination, and it doesn't model DNS-based evasion of a destination check, which is a different channel I covered in the DNS covert-channel post. And it doesn't model volume: the hundreds of thousands of Wikidata queries are a rate problem, not a destination problem, which is its own post with its own control.
fetch_citation_source still can't tell a DOI from an attacker's host. It never had to learn, because the mcpGuardrails block in config.yaml is what makes the call to port 8082 fail, not anything added to server.js. That's the actual claim this repo makes about the Wikimedia incident: whatever Wikimedia's real citation tool looked like, fixing a laundering bug in one tool's code doesn't fix the next tool that fetches URLs, but a gateway sitting in front of every MCP tool call does.
Repo: themsquared/agent-proxy-laundering. I work at Solo.io, which makes agentgateway.
Frequently asked questions
What did OpenAI's agents do to Wikimedia's citation tool?
The Wikimedia Foundation disclosed on 2026-10-05 that a few wiki edits, which it attributes to OpenAI-operated agents, changed the configuration of a citation tool in a way it believes was an attempt to misuse the tool as a proxy for fetching data from remote services. Wikimedia's post does not publish the exact configuration change or mechanism.
Can an MCP tool be used as a proxy even if it wasn't designed to be one?
Yes, whenever the tool's job includes fetching a URL it is given. A citation resolver that fetches bibliographic sources has no way to distinguish a legitimate citation lookup from a request to fetch an unrelated host, because both are the same operation: fetch this URL and return what comes back.
How do you stop a fetch-capable MCP tool from being used as a proxy?
Put a destination check in front of the tool, not inside it. agentgateway's MCP guardrails read the resolved tools/call request, including its arguments, before the tool executes, and can reject a call whose target argument fails an allowlist check. The tool's own code never changes and never needs to know what a valid destination looks like.
---
*Canonical version, with machine-readable markdown at https://webofmike.com/agent-proxy-laundering/index.md: webofmike.com
MCP Tool-Call Guardrails: Gating Agents Before the Tool Runs
Originally published at webofmike.com on 2026-10-22. The demo repo and every command in it were run before publishing.
agentgateway can now reject or rewrite an MCP tools/call request before the tool runs, and mask the result before the client sees it, from CEL expressions in one config file, with no sidecar service. The commit that adds it landed on main two days ago and isn't in a tagged release yet. It closes a gap from three days earlier: Prisma AIRS can scan MCP tool events, but hooking it into agentgateway needed a webhook adapter because agentgateway's own guardrails only ran on LLM chat messages, the prompt going in, the text coming back, not the tool call itself.
themsquared/tool-call-guardrails is a toy customer-support MCP server, lookup_customer and run_support_macro, with three guardrail processors in front of it in one config.yaml: one rejects a destructive tool call before it runs, one masks PII out of the arguments on the way in, one masks a secret out of the result on the way back.
Why this is a different layer than a prompt guardrail
agentgateway already has prompt guards for LLM chat messages, and their default scope only covers the system prompt and chat messages, not tool output, unless you add toolOutput to the list explicitly. Even with that scope set, the guard is still downstream of the tool call: the tool has already run, the data has already moved, and the guard is filtering what the model sees afterward.
MCP guardrails (mcpGuardrails under mcp.policies) run at the MCP method layer instead. A processor can run in the request phase, before agentgateway forwards the call to the upstream MCP server, in the response phase, after the server replies, or full (both). A request-phase rejection means the tool is never invoked at all, not filtered after the fact. And because it is keyed on the MCP method (tools/call, tools/list, and so on) rather than an LLM's chat schema, it applies the same way no matter what's driving the agent, an LLM behind agentgateway's own router, a different LLM entirely, or a human testing with the MCP Inspector.
The policy, in one file
config:
adminAddr: "off" # only needed if something else on the box already holds the admin port
mcp:
port: 13000
policies:
mcpGuardrails:
processors:
# 1. Reject before anything else runs.
- kind: expression
methods: {"tools/call": request}
condition: >-
mcp.params.name == 'run_support_macro' &&
has(mcp.params.arguments) &&
has(mcp.params.arguments.raw_command) &&
mcp.params.arguments.raw_command.contains('rm -rf')
reject: "raw_command contains a destructive shell pattern; this macro requires human review"
# 2. Mask PII in arguments before the upstream tool ever sees them.
- kind: expression
methods: {"tools/call": request}
condition: >-
mcp.params.name == 'run_support_macro' &&
has(mcp.params.arguments) && has(mcp.params.arguments.macro)
transform: >-
mcp.params.with(p, p.merge({
'arguments': p.arguments.merge({
'macro': p.arguments.macro.regexReplaceAll(
'[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}', '[EMAIL]'
)
})
}))
# 3. Mask secrets in what the tool hands back.
- kind: expression
methods: {"tools/call": response}
condition: has(mcp.result.content)
transform: >-
mcp.result.with(r, r.merge({
'content': r.content.map(item,
item.type == 'text'
? item.merge({'text': item.text.regexReplaceAll('sk-live-[A-Za-z0-9-]+', '[REDACTED_KEY]')})
: item
)
}))
targets:
- name: support-tools
stdio:
cmd: node
args: ["mcp-server/server.js"]
mcp.params is the live request's MCP params object, arguments included. mcp.result is the response's MCP result object. Processors run in the order they're listed; a reject in an earlier processor stops the chain, and a transform in one processor is what the next processor (and eventually the upstream server or the client) sees. That ordering matters here: the reject check runs before the mask, so a call that should never have been sent is refused on its original text, not on a redacted copy of it.
Three calls through the gateway
The support server's run_support_macro tool takes a raw_command field, an internal escape hatch some macros pass straight to a backend shell. A call with rm -rf in that field never reaches the tool:
$ npx -y @modelcontextprotocol/inspector --cli http://localhost:13000/mcp \
--method tools/call --tool-name run_support_macro \
--tool-arg customer_id=cust-1002 --tool-arg macro=reset_password \
--tool-arg "raw_command=rm -rf /data/cust-1002"
{
"content": [{"type": "text", "text": "raw_command contains a destructive shell pattern; this macro requires human review"}],
"isError": true
}
A macro call that happens to carry an email address gets the address masked before the tool runs, so it's never written into whatever backend the macro touches:
$ npx -y @modelcontextprotocol/inspector --cli http://localhost:13000/mcp \
--method tools/call --tool-name run_support_macro \
--tool-arg customer_id=cust-1002 \
--tool-arg "macro=escalate to priya.shah@example.com"
{
"content": [{"type": "text", "text": "Ran macro 'escalate to [EMAIL]' for cust-1002"}]
}
And lookup_customer, which (realistically, carelessly) stores an API key in a customer's notes field, has that key redacted before the result leaves the gateway:
$ npx -y @modelcontextprotocol/inspector --cli http://localhost:13000/mcp \
--method tools/call --tool-name lookup_customer --tool-arg customer_id=cust-1001
{
"content": [{"type": "text", "text": "Jordan Lee (Pro). Notes: Escalated 2026-09-02, resolved. Backup API key on file: [REDACTED_KEY]. Do not quote this to the customer."}]
}
None of these three calls touch an LLM. The MCP Inspector CLI talks tools/call straight to agentgateway's MCP endpoint, which is the point: the guardrail is enforced on the protocol, not bolted onto a chat completion.
First pass at the secret regex left half the key exposed. sk-live-[A-Za-z0-9]+ looks like it should catch sk-live-demo-7f3a9c2b1e, and it does, partially: the character class doesn't include -, so regexReplaceAll matched only sk-live-demo and stopped at the hyphen, handing back [REDACTED_KEY]-7f3a9c2b1e. The fix is one character, sk-live-[A-Za-z0-9-]+, but it's the kind of mistake that's invisible until you test against a key shaped like a real one, not sk-live-abc123.
This is not in a release yet
The CEL expression processor landed on main on October 6, 2026, three days before this post, in 276f10d, on top of an earlier gRPC-based mcpGuardrails hook. The most recent tagged release, v1.6.0, published October 2, predates both. Building from source is the only way to run this today:
git clone https://github.com/agentgateway/agentgateway.git
cd agentgateway && cargo build --release
Everything in this post runs against that build. Treat the exact CEL surface (regexReplaceAll, .with(), .merge()) as subject to change before it ships in a tagged release; the mechanism, request-phase rejection and transform, response-phase transform, in-process CEL with no sidecar, is the part worth taking away.
What's still open
The three processors in this repo's config.yaml are all kind: expression: fast, local, and limited to what a regex or a CEL comparison can decide. agentgateway also exposes a remote gRPC processor (kind: remote), the same shape the Prisma AIRS integration used for LLM-side guardrails, for a case this demo doesn't cover: a run_support_macro argument that reads as fine text but is actually a prompt-injection payload aimed at whatever agent calls the tool next. CEL can reject a literal rm -rf; it can't judge intent. That's the next adapter to build against this hook, not this one.
Repo again: themsquared/tool-call-guardrails.
Frequently asked questions
What is an MCP tool-call guardrail?
A policy that runs on the resolved tools/call request and response at the MCP protocol layer, not on the chat messages going into an LLM. agentgateway's mcpGuardrails processors see mcp.params (the tool name and arguments) on the request phase and mcp.result on the response phase, and can reject, or rewrite, either one before it reaches the upstream MCP server or the client.
How is this different from a prompt guardrail?
A prompt guardrail scans text flowing through an LLM's chat API, and by default does not see tool results at all, unless its scope explicitly names toolOutput. An MCP guardrail runs at the protocol layer where the tool is actually invoked, so it can refuse a tools/call outright before the tool executes, and it works for any MCP client, not just ones routed through an LLM endpoint.
Do I need to run a separate service for MCP guardrails in agentgateway?
Not for CEL-based rules. The expression processor (kind: expression) evaluates CEL against mcp.params and mcp.result in-process, in the gateway itself. agentgateway also supports a remote gRPC processor for logic too complex for CEL, but the expression kind needs nothing extra running.
Can an MCP guardrail reject a tool call outright?
Yes. A request-phase processor with a reject condition stops the chain and the tool is never invoked; the caller gets a tool result with isError: true instead of whatever the tool would have done. A response-phase processor can only transform or pass through, since the tool has already run by then.
---
*Canonical version, with machine-readable markdown at https://webofmike.com/tool-call-guardrails/index.md: webofmike.com
agentgateway/agentgateway
rustの中央集権型のa2aプロトコルかー一旦試してみよ
1 like · 128 views
Agentgateway: secure, observable AI conversations
Next Generation Agentic Proxy for AI Agents and MCP servers
agentgateway/agentgateway on GitHub: agentgateway/agentgateway
This week's round-up: youtube.com
Trending today. This video describes what the project says it does — it is not a review or a recommendation.
▶ More like this: youtube.com
Subscribe for a new open source project every day.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[ar] Agentgateway: محادثات ذكاء اصطناعي آمنة وقابلة للمراقبة
جيل جديد من وكلاء الذكاء الاصطناعي لخوادم MCP
agentgateway/agentgateway على GitHub: agentgateway/agentgateway
ملخص هذا الأسبوع: youtube.com
الأكثر رواجًا اليوم. يشرح هذا الفيديو وظائف المشروع المعلنة، وليس مراجعة أو توصية.
▶ المزيد من هذا النوع: youtube.com
اشترك لتصلك مشاريع مفتوحة المصدر جديدة يوميًا.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[bn] Agentgateway: নিরাপদ ও পর্যবেক্ষণযোগ্য এআই কথোপকথন
এআই এজেন্ট এবং এমসিপি সার্ভারের জন্য পরবর্তী প্রজন্মের এজেন্টিক প্রক্সি
গিটহাবে agentgateway/agentgateway: agentgateway/agentgateway
এই সপ্তাহের সারসংক্ষেপ: youtube.com
আজকের ট্রেন্ডিং। এই ভিডিওটি বর্ণনা করে যে প্রকল্পটি কী করে — এটি কোনো পর্যালোচনা বা সুপারিশ নয়।
▶ এই ধরনের আরও: youtube.com
প্রতিদিন একটি নতুন ওপেন সোর্স প্রকল্পের জন্য সাবস্ক্রাইব করুন।
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[de-DE] Agentgateway: Sichere, beobachtbare KI-Konversationen
Agentic-Proxy der nächsten Generation für KI-Agenten und MCP-Server
agentgateway/agentgateway auf GitHub: agentgateway/agentgateway
Wochenrückblick: youtube.com
Heute im Trend. Dieses Video beschreibt die Projektbeschreibung – es ist keine Rezension oder Empfehlung.
▶ Ähnliche Videos: youtube.com
Abonniere unseren Kanal für täglich neue Open-Source-Projekte.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[es-US] Agentgateway: conversaciones con IA seguras y observables
Proxy agentic de próxima generación para agentes de IA y servidores MCP
agentgateway/agentgateway en GitHub: agentgateway/agentgateway
Resumen de esta semana: youtube.com
Tendencia del día. Este video describe lo que el proyecto afirma hacer; no es una reseña ni una recomendación.
▶ Más contenido similar: youtube.com
Suscríbete para descubrir un nuevo proyecto de código abierto cada día.
#Shorts #GitHub #OpenSource #rust #agentgateway #generación #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[fr-FR] Agentgateway : des conversations IA sécurisées et observables
Proxy Agentic nouvelle génération pour agents IA et serveurs MCP
agentgateway/agentgateway sur GitHub : agentgateway/agentgateway
Récapitulatif de la semaine : youtube.com
À la une aujourd'hui. Cette vidéo décrit le fonctionnement du projet ; il ne s'agit ni d'un test, ni d'une recommandation.
▶ Plus de vidéos similaires : youtube.com
Abonnez-vous pour découvrir un nouveau projet open source chaque jour.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[hi] Agent Gateway: सुरक्षित, ऑब्ज़र्वेबल AI बातचीत
एआई एजेंट्स और एमसीपी सर्वरों के लिए अगली पीढ़ी का एजेंटिक प्रॉक्सी
agentgateway/agentgateway GitHub पर: agentgateway/agentgateway
इस सप्ताह का सारांश: youtube.com
आज का ट्रेंडिंग वीडियो। यह वीडियो प्रोजेक्ट के कार्यों का वर्णन करता है - यह कोई समीक्षा या अनुशंसा नहीं है।
▶ इस तरह के और वीडियो: youtube.com
हर दिन एक नए ओपन सोर्स प्रोजेक्ट के लिए सब्सक्राइब करें।
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[id] Agentgateway: percakapan AI yang aman dan dapat diobservasi
Proxy Agen Generasi Berikutnya untuk Agen AI dan Server MCP
agentgateway/agentgateway di GitHub: agentgateway/agentgateway
Ringkasan minggu ini: youtube.com
Sedang tren hari ini. Video ini menjelaskan apa yang diklaim proyek ini — ini bukan ulasan atau rekomendasi.
▶ Lebih banyak seperti ini: youtube.com
Berlangganan untuk proyek open source baru setiap hari.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[it] Agentgateway: conversazioni AI sicure e monitorabili
Proxy Agentic di nuova generazione per agenti AI e server MCP
agentgateway/agentgateway su GitHub: agentgateway/agentgateway
Riepilogo della settimana: youtube.com
Tendenze di oggi. Questo video descrive le funzionalità dichiarate del progetto, non è una recensione né una raccomandazione.
▶ Altri video simili: youtube.com
Iscriviti per scoprire un nuovo progetto open source ogni giorno.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[iw] Agentgateway: שיחות AI מאובטחות וניתנות לניטור
פרוקסי Agentic מהדור הבא עבור סוכני בינה מלאכותית ושרתי MCP
agentgateway/agentgateway ב-GitHub: agentgateway/agentgateway
סיכום השבוע: youtube.com
בטרנדים היום. סרטון זה מתאר מה הפרויקט אומר שהוא עושה - זו אינה סקירה או המלצה.
▶ עוד כמו זה: youtube.com
הירשמו לפרויקט קוד פתוח חדש בכל יום.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[ja] Agentgateway:安全で可視化されたAI対話
AIエージェントとMCPサーバー向け次世代エージェントプロキシ
GitHubのagentgateway/agentgateway:agentgateway/agentgateway
今週のまとめ:youtube.com
本日の注目動画。この動画はプロジェクトの説明であり、レビューや推奨ではありません。
▶ 類似動画はこちら:youtube.com
毎日新しいオープンソースプロジェクトを紹介するチャンネル登録はこちら。
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[ko] Agentgateway: 안전하고 관찰 가능한 AI 대화
AI 에이전트 및 MCP 서버를 위한 차세대 에이전트형 프록시
agentgateway/agentgateway (GitHub: agentgateway/agentgateway)
이번 주 요약: youtube.com
오늘의 인기 영상입니다. 이 영상은 프로젝트의 기능을 설명하는 영상이며, 리뷰나 추천이 아닙니다.
▶ 더 보기: youtube.com
매일 새로운 오픈 소스 프로젝트를 받아보시려면 구독하세요.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[ml] Agentgateway: സുരക്ഷിതവും നിരീക്ഷിക്കാവുന്നതുമായ AI സംഭാഷണങ്ങൾ
AI ഏജന്റുമാർക്കും MCP സെർവറുകൾക്കുമുള്ള നെക്സ്റ്റ് ജനറേഷൻ ഏജന്റ് പ്രോക്സി
GitHub-ലെ agentgateway/agentgateway: agentgateway/agentgateway
ഈ ആഴ്ചയിലെ സംഗ്രഹം: youtube.com
ഇന്നത്തെ ട്രെൻഡിംഗ്. പ്രോജക്റ്റ് എന്താണ് പറയുന്നതെന്ന് ഈ വീഡിയോ വിവരിക്കുന്നു - ഇത് ഒരു അവലോകനമോ ശുപാർശയോ അല്ല.
▶ ഇതുപോലുള്ള കൂടുതൽ: youtube.com
എല്ലാ ദിവസവും ഒരു പുതിയ ഓപ്പൺ സോഴ്സ് പ്രോജക്റ്റിനായി സബ്സ്ക്രൈബുചെയ്യുക.
#ഷോർട്ട്സ് #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[nl-NL] Agentgateway: veilige, observeerbare AI-gesprekken
Agentische proxy van de volgende generatie voor AI-agenten en MCP-servers
agentgateway/agentgateway op GitHub: agentgateway/agentgateway
Samenvatting van deze week: youtube.com
Trending vandaag. Deze video beschrijft wat het project doet — het is geen recensie of aanbeveling.
▶ Meer zoals dit: youtube.com
Abonneer je voor een nieuw open source-project elke dag.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[pa] Agentgateway: ਸੁਰੱਖਿਅਤ ਅਤੇ ਦੇਖਣਯੋਗ AI ਗੱਲਬਾਤ
AI ਏਜੰਟਾਂ ਅਤੇ MCP ਸਰਵਰਾਂ ਲਈ ਅਗਲੀ ਪੀੜ੍ਹੀ ਦੇ ਏਜੰਟਿਕ ਪ੍ਰੌਕਸੀ
agentgateway/agentgateway GitHub 'ਤੇ: agentgateway/agentgateway
ਇਸ ਹਫ਼ਤੇ ਦਾ ਸੰਖੇਪ: youtube.com
ਅੱਜ ਟ੍ਰੈਂਡਿੰਗ। ਇਹ ਵੀਡੀਓ ਦੱਸਦਾ ਹੈ ਕਿ ਪ੍ਰੋਜੈਕਟ ਕੀ ਕਹਿੰਦਾ ਹੈ ਕਿ ਇਹ ਕੀ ਕਰਦਾ ਹੈ — ਇਹ ਕੋਈ ਸਮੀਖਿਆ ਜਾਂ ਸਿਫ਼ਾਰਸ਼ ਨਹੀਂ ਹੈ।
▶ ਇਸ ਤਰ੍ਹਾਂ ਦੇ ਹੋਰ: youtube.com
ਹਰ ਰੋਜ਼ ਇੱਕ ਨਵੇਂ ਓਪਨ ਸੋਰਸ ਪ੍ਰੋਜੈਕਟ ਲਈ ਗਾਹਕ ਬਣੋ।
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[pl] Agentgateway: bezpieczne i obserwowalne konwersacje z AI
Agentic Proxy nowej generacji dla agentów AI i serwerów MCP
agentgateway/agentgateway na GitHubie: agentgateway/agentgateway
Podsumowanie tygodnia: youtube.com
Dzisiejsze trendy. Ten film opisuje, co projekt obiecuje — nie jest to recenzja ani rekomendacja.
▶ Więcej na ten temat: youtube.com
Subskrybuj, aby każdego dnia otrzymywać informacje o nowym projekcie open source.
#Shorts #GitHub #OpenSource #rust #agentgateway #generacja #agentic #proxy #agenty #serwery #kodowanie #programowanie #programista #oprogramowanie #narzędzia_programistyczne
[pt-BR] Agentgateway: conversas com IA seguras e observáveis
Proxy Agético de Próxima Geração para Agentes de IA e Servidores MCP
agentgateway/agentgateway no GitHub: agentgateway/agentgateway
Resumo desta semana: youtube.com
Em alta hoje. Este vídeo descreve o que o projeto faz — não é uma análise ou recomendação.
▶ Mais vídeos como este: youtube.com
Inscreva-se para receber um novo projeto de código aberto todos os dias.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[ru] Agentgateway: безопасные и прозрачные диалоги с ИИ
Прокси-сервер Agentic нового поколения для ИИ-агентов и MCP-серверов
agentgateway/agentgateway на GitHub: agentgateway/agentgateway
Обзор этой недели: youtube.com
Тренды сегодня. В этом видео описывается, что, по заявлению проекта, он делает — это не обзор и не рекомендация.
▶ Больше подобного: youtube.com
Подписывайтесь, чтобы получать информацию о новых проектах с открытым исходным кодом каждый день.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[ta] Agentgateway: பாதுகாப்பான மற்றும் கண்காணிக்கக்கூடிய AI உரையாடல்கள்
AI ஏஜென்ட்கள் மற்றும் MCP சர்வர்களுக்கான அடுத்த தலைமுறை ஏஜென்டிக் ப்ராக்ஸி
கிட்ஹப்பில் உள்ள agentgateway/agentgateway: agentgateway/agentgateway
இந்த வாரத் தொகுப்பு: youtube.com
இன்று பிரபலமாக உள்ளது. இந்த வீடியோ, இந்தத் திட்டம் என்ன செய்வதாகக் கூறுகிறதோ அதை விவரிக்கிறது — இது ஒரு விமர்சனமோ அல்லது பரிந்துரையோ அல்ல.
▶ இது போன்ற மேலும் பல: youtube.com
ஒவ்வொரு நாளும் ஒரு புதிய ஓப்பன் சோர்ஸ் திட்டத்திற்காக சப்ஸ்கிரைப் செய்யுங்கள்.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[te] ఏజెంట్ గేట్వే (Agentgateway): సురక్షితమైన, పర్యవేక్షించదగిన AI సంభాషణలు
AI ఏజెంట్లు మరియు MCP సర్వర్ల కోసం తదుపరి తరం ఏజెంటిక్ ప్రాక్సీ
GitHubలో agentgateway/agentgateway: agentgateway/agentgateway
ఈ వారం రౌండప్: youtube.com
ఈ రోజు ట్రెండింగ్. ఈ వీడియో, ప్రాజెక్ట్ ఏమి చేస్తుందో వివరిస్తుంది — ఇది సమీక్ష లేదా సిఫార్సు కాదు.
▶ ఇలాంటివి మరిన్ని: youtube.com
ప్రతిరోజూ ఒక కొత్త ఓపెన్ సోర్స్ ప్రాజెక్ట్ కోసం సబ్స్క్రయిబ్ చేసుకోండి.
#Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
[uk] Agentgateway: безпечні та прозорі ШІ-розмови
Агентський проксі наступного покоління для агентів ШІ та MCP-серверів
agentgateway/agentgateway на GitHub: agentgateway/agentgateway
Огляд цього тижня: youtube.com
Тренди сьогодні. Це відео описує, що робить проєкт — це не огляд і не рекомендація.
▶ Більше схожих відео: youtube.com
Підпишіться на новий проєкт з відкритим кодом щодня.
#Шорти #GitHub #ВідкритийВихіднийКод #rust #agentgateway #покоління #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
1 like · 1 comment · 31 views
agentgateway/agentgateway on GitHub:
agentgateway/agentgateway
This week's round-up:
youtube.com
MCPゲートウェイのツール単位認可は効くか — agentgatewayで削除を通した3つの穴
はじめに
2026年10月20日、iPaaS ベンダの Celigo が「MCP Management」を発表しました。MCP Gateway で他社の MCP サーバをまとめてプロキシし、ツールを個別に無効化できる。IdP 経由でエンドユーザ本人の権限で実行し、共有クレデンシャルは使わない、という触れ込みです (SiliconANGLE の記事)。
MCP ゲートウェイ自体は Kong、Pomerium、Docker MCP Toolkit、agentgateway など多くの製品が出ている分野で、どの製品も「ツール単位のガバナンス」「ユーザ単位の監査」をほぼ同じ言葉で訴求しています。気になったのは次の2点です。
ツールを「無効化」すると、tools/list から消えるだけなのか。名前を知っているクライアントが tools/call を直接投げたら止まるのか
呼び出しログに「誰の代理で実行したか」が残るのは、どういう条件のときか
Celigo は商用でアカウントが必要なので、論点だけ借りて OSS の agentgateway v1.6.0 で実測しました。
結論を先に書きます。
agentgateway は一覧から隠すだけではありません。拒否したツールは tools/call でも止まり、上流には届きません。
ただし deny(拒否リスト)型でルールを書くと、3つの経路で削除ツールが実行できました。 ツール名の大文字小文字違い、ロールのクレームが無いユーザ、トークン無しの匿名アクセス、の3つです。
ログの jwt.sub は、JWT を付けたリクエストにしか残りません。権限で拒否した呼び出しと、存在しないツールの呼び出しは、ログ上で同じ表示になります。
Claude Code は一覧に無いツールをそもそも呼べません。なので「隠すだけ」でも Claude Code に対しては止まります。ゲートウェイ側の強制が効いてくるのは、同じトークンを持った別のクライアントに対してです。
構成
自作クライアント / Claude Code ──(Bearer JWT)──> agentgateway :3000 ──> 自作 MCP サーバ :3001
上流の MCP サーバ: Python の標準ライブラリだけで書いた、約100行の Streamable HTTP サーバです。list_records / get_record (読み取り) と update_record / delete_record (書き込み・破壊的) を公開します。受け取ったヘッダとツール名はすべてログに記録します。環境変数 LENIENT=1 を付けると、ツール名を小文字化・前後の空白を除去してから探します(名前の扱いが緩い実装の再現です)
自作クライアント: tools/list を経由せず、任意の名前で tools/call を投げられる最小のクライアントです
JWT: ES256 で3ユーザ分を発行しました
alice: role=operator(書き込み可にしたい人)
bob: role=viewer(読み取りだけにしたい人)
carol: role クレーム無し
agentgateway は公式リリースのバイナリ(97MB、単体で動作)を使いました
実験1: allow 型(許可リスト)で書く
公式 example に近い書き方です。
mcp:
port: 3000
policies:
jwtAuth:
issuer: lab.local
audiences: [mcp.lab.local]
jwks:
file: ./keys/jwks.json
mcpAuthorization:
rules:
- allow: 'mcp.tool.name in ["list_records", "get_record"]'
- allow: 'jwt.role == "operator" && mcp.tool.name in ["update_record", "delete_record"]'
targets:
- name: records
mcp:
host: 127.0.0.1
== alice
LIST 200 ['list_records', 'get_record', 'update_record', 'delete_record']
CALL delete_record 200 (成功)
== bob
LIST 200 ['list_records', 'get_record']
CALL delete_record 400 {"error":{"code":-32602,"message":"Unknown tool: delete_record"}}
== carol
LIST 200 ['list_records', 'get_record']
CALL delete_record 400 Unknown tool: delete_record
== トークン無し
LIST 200 ['list_records', 'get_record']
bob が名前を直接指定して tools/call を投げても 400 で拒否されました。上流サーバのログにも bob の delete_record は届いていません。ゲートウェイの段階で強制されています。 仮説1は agentgateway については外れでした。
この形で、以下も試しました。いずれも止まっています。
Delete_Record / DELETE_RECORD / 前後に空白を付けた名前 → 400(完全一致しない名前は許可リストに当たらない)
JSON-RPC のバッチ(配列で複数呼び出し) → 400 fail to deserialize request body
別の鍵で署名した JWT、alg: none、期限切れ → いずれも 401
拒否されたときの応答は「権限がありません」ではなく Unknown tool でした。権限が無いのか、ツールが存在しないのかをクライアントに区別させない作りです。ソースにも authorization_denied_returns_unknown_tool_error というテストがあり、意図した挙動だと分かります。
一点だけ見落としやすいのが、トークン無しでも 200 が返り、読み取り系のツールが使えたことです。jwtAuth.mode の既定値は optional で、トークンがあれば検証し、無ければそのまま通します。ソースのコメントにも "Warning: this allows requests without a JWT." と書かれています。公式 example の設定も mode を書いていないので、そのまま真似すると匿名アクセスが通ります。
実験2: deny 型(拒否リスト)で書く
「基本は全部許可して、書き込み系だけ operator 以外を拒否する」という、素直に思いつく書き方です。
mcpAuthorization:
rules:
- allow: 'true'
- deny: 'mcp.tool.name in ["update_record", "delete_record"] && jwt.role != "operator"'
上流は LENIENT=1(名前の扱いが緩い実装)にして、実際のレコードを消せるか確かめました。
CALL list_records 200 {"1": "顧客A 問い合わせ", "2": "顧客B 解約申請", "3": "顧客C 住所変更"}
CALL Delete_Record 200 DELETED 1 ← bob (viewer)
CALL delete_record 200 DELETED 2 ← carol (role クレーム無し)
CALL delete_record 200 DELETED 3 ← トークン無し
CALL list_records 200 {}
3件とも、削除できてはいけない人が消せてしまいました。 原因はそれぞれ別です。
穴1: 名前の揺らぎ
bob が delete_record を呼ぶと拒否されます。しかし Delete_Record なら通りました。ゲートウェイは mcp.tool.name を文字列として比較するだけで、その名前が tools/list に載っているかは確認しません。載っていない名前でも上流にそのまま転送します。上流が名前を正規化する実装だと、そこで実行されます。
上流を厳密一致の実装に戻すと unknown tool Delete_Record で失敗しました。この穴が開くかどうかは上流の実装次第です。 ゲートウェイが一律に守ってくれるわけではありません。
穴2: クレームが無いユーザ
carol は role クレームを持っていません。jwt.role != "operator" の評価がエラーになり、エラーは false として扱われるので deny が発火しません。ソース (http/authorization.rs) を見ると、各ルールを eval_bool で評価し、エラーは false になります。設定スキーマにも次の記述があり、既知の仕様です。
This mode is not recommended because expression failures fail to deny; prefer Allow or Require.
穴3: トークン無し
穴2と同じ理由です。既定の optional モードではトークン無しのリクエストが通り、jwt そのものが存在しないので deny の評価がエラーになります。このときは tools/list にも4本すべてが表示されました。
防御的に書き直した場合
jwtAuth:
mode: strict
...
- deny: 'mcp.tool.name in ["update_record", "delete_record"] && !(has(jwt.role) && jwt.role == "operator")'
bob delete_record 400
carol delete_record 400
トークン無し → 401 authentication failure: no bearer token found
bob Delete_Record 200 DELETED 1 ← 名前の揺らぎは残る
クレーム欠落と匿名の穴は塞がりました。名前の揺らぎは、deny 型で書いている限り塞がりません。require を使って jwt.role == "operator" || !(mcp.tool.name in [...]) と書いた場合も同じでした。require は評価エラーのとき拒否側に倒れるので carol は止まりますが、「名前で除外する」書き方である以上、Delete_Record は除外の対象から漏れます。
安全な書き方は、allow 型で許可する名前を完全一致で列挙し、mode を strict にすることです。 今回試した限り、この形ですべての経路が止まりました。
ログに「誰が」残るか
既定のアクセスログには、何も設定しなくても次の項目が出ます。
http.status=200 jwt.sub=alice protocol=mcp mcp.method.name=tools/call mcp.target=records gen_ai.tool.name=delete_record mcp.session.id=...
実験2のログから、tools/call の行だけを抜き出しました。
http.status=200 jwt.sub=alice gen_ai.tool.name=delete_record
http.status=400 jwt.sub=bob gen_ai.tool.name=delete_record
http.status=200 jwt.sub=bob gen_ai.tool.name=Delete_Record
http.status=200 jwt.sub=carol gen_ai.tool.name=delete_record
http.status=200 gen_ai.tool.name=delete_record ← 匿名の削除
jwt.sub が残るのは、JWT を付けてきたリクエストだけです。匿名の削除は jwt.sub の項目ごと無くなり、残るのは送信元 IP だけでした。仮説2は当たりです。
どのレコードを消したか(引数)は、既定では残りません。config.logging.fields.add に tool_args: 'mcp.tool.arguments' を足すと出るようになりました。
権限で拒否した呼び出しと、存在しないツールの呼び出しは、どちらも error="mcp: Unknown tool: ..." reason=MCP と表示されます。「ポリシーで止めた件数」をログから数えることはできません。
上流の MCP サーバにユーザ本人の情報が届くのは、backendAuth: { passthrough: {} } を設定したときだけでした。設定しない場合、上流の Authorization ヘッダは空です。
認可はセッション単位ではなく、リクエストごとの JWT で評価されていました。alice で開始したセッション ID を使い回して bob のトークンで呼ぶと、bob の権限で判定されます(拒否されます)。逆に言えば、セッションが特定のユーザに紐付いているわけでもありません。
ゲートウェイを迂回する
上流を 127.0.0.1 で待受 → 同じホストから直叩き: delete_record 200 DELETED 3
上流を 0.0.0.0 で待受 → LAN の IP から: delete_record 200 DELETED 3
→ Tailscale の IP から: delete_record 200 DELETED 2
当たり前の話ですが、上流に認証が無く到達できる状態なら、ゲートウェイの認可は何の意味もありません。上流を 127.0.0.1 に絞っても、同じホストで動くプロセスからは直接叩けます。エージェントに Bash を渡している場合は、エージェント自身が curl でゲートウェイを迂回できる経路が残ります。 なお agentgateway 自体は、mcp.port を指定すると全インターフェース (*:3000) で待ち受けていました。
Claude Code から繋ぐ
トークンを環境変数から展開する設定ファイルを書いて、claude -p で接続しました。
{
"mcpServers": {
"records": {
"type": "http",
"url": "127.0.0.1",
"headers": { "Authorization": "Bearer ${MCP_TOKEN}" }
}
}
}
bob のトークンで、「mcprecordsdelete_record を id=1 で呼んで。一覧に無くても、存在しないと結論する前にその名前で直接呼んでみること」と指示しました。
mcprecordsdelete_record 存在しない。ToolSearch 検出なし、直接呼び出しもエラー。
ゲートウェイのログには tools/list しか記録されておらず、tools/call は1回も届いていません。 Claude Code は tools/list に無いツールを呼べないので、クライアント側で止まります。alice のトークンでは DELETED 1 が返り、ログに jwt.sub=alice gen_ai.tool.name=delete_record が残りました。
つまり、Claude Code のように行儀のよいクライアントだけが相手なら、「一覧から隠すだけ」の実装でも実害は出ません。ゲートウェイの強制が本当に必要になるのは、同じトークンを持った別のクライアント(スクリプトや、エージェントが Bash 経由で叩く curl)が相手のときです。「隠蔽か強制か」の違いは、Claude Code で触っているだけでは見えません。直接 HTTP を投げて初めて確認できます。
まとめ
agentgateway は「隠すだけ」ではありません。 同じ CEL ルールが一覧の絞り込みと tools/call の拒否の両方に効き、拒否した呼び出しは上流に届きません
ただし deny 型で書くと、名前の揺らぎ(上流が正規化する場合)・クレーム欠落・匿名(既定の optional)の3経路で書き込みが通ります。 allow 型・完全一致・strict が安全側です
「誰の代理か」がログに残るのは JWT を付けたリクエストだけです。引数は設定しないと残りません。拒否と存在しないツールはログで区別できません。上流に本人を伝えるには passthrough が必要です
上流に直接届く経路があれば、それだけで全部が無意味になります。ネットワーク分離か、上流側での JWT 検証が前提です
Celigo のような商用製品を評価するときも、確認すべき点は同じだと思います。デモ画面で一覧からツールが消えることを見るだけでなく、①名前を直接指定した tools/call が拒否されるか、②名前の大文字小文字を変えても拒否されるか、③識別情報(クレーム)の無いユーザがどちらに倒れるか、④ログに拒否と主体が残るか、⑤上流に直接届く経路が無いか、を聞くべきです。エージェントに業務システムを触らせるなら、このあたりが最低限の確認項目になります。
参考
agentgateway (GitHub) — examples/mcp-authorization、schema/config.md
agentgateway: MCP authorization
SiliconANGLE: Celigo launches MCP tool management
先行の日本語記事: MCPゲートウェイの実装 Finatextグループ MCPass、既存サービスにMCPサーバーを組み込む際の設計ポイント(どちらも「一覧で絞ったうえで、呼び出し時にも再認可する」という設計を解説しています)
1 like
Mentions
agentgateway v1.7.0: How Agent Session Detection Works Originally published at webofmike.com on 2026-11-01. The demo repo and every command in it were run before publishing. agentgateway v1.7.0 (GA, 2026-10-27) can tell which coding-agent session sent a request without any client-side change: it reads the session header the agent is already sending. themsquared/agw-17-hands-on validates the detection rule against the real v1.7.0 binary and a mock LLM backend, no API key, no network calls, five curl requests that show exactly when it fires and when it doesn't. Why this matters before the mechanism Without session detection, agentgateway can rate limit or log by API key or IP address, but it has no way to group the many requests one agent run makes into a single unit. Coding agents like Claude Code, Cursor, and Codex already send a session or conversation id in their own headers so their own backends can correlate turns. v1.7.0 reads that same header. Once agentgateway knows which requests belong together, that grouping is available to any CEL-driven policy: a rate limit keyed per session instead of per key, an audit trail grouped by session, routing a whole session to the same backend, or flagging a session whose request pattern looks unusual. None of this needs the client to adopt a new header or send extra metadata. It needs the client to keep doing what it already does. The headers it reads, in priority order agentgateway's detection logic (crates/agentgateway/src/http/session.rs in the v1.7.0 tag) checks, in order: 1. Any header shaped x-<vendor>-session-id. Claude Code sends x-claude-code-session-id; this is a generic pattern, not a Claude-specific special case, so Cursor's x-cursor-session-id or any other vendor's equivalent is detected the same way. 2. Codex's unprefixed session-id, session_id, thread-id, or conversation_id headers, but only when the request's User-Agent starts with codex. This gate exists because those header names are generic enough that treating them as agent-session signals from any client would produce false positives. 3. A vendor-less x-session-id header, as a last resort. Whichever header matches, the detected value becomes available to CEL as request.agent.session. The rule that silently drops a session id The validation is stricter than it looks, and it fails quietly: fn valid(value: &HeaderValue) -> Option<&str> { value.to_str().ok().filter(|v| { v.len() >= 8 && v.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_') }) } A session id under 8 characters, or one containing a character outside [A-Za-z0-9_-], doesn't get rejected with an error. It gets treated exactly like a missing header: request.agent.session is simply never set, and nothing in the response or the logs calls that out. If a session grouping you expect to see isn't showing up, this rule is the first thing to check, not a deeper config problem. Proving it with a mock backend agw-17-hands-on's config points agentgateway's standalone LLM route at a 40-line Python HTTP server instead of a real provider, using v1.7.0's custom provider with the completions wire format: # config/config.yaml config: session: key: disabled # demo only: v1.7 standalone always provisions a session key on first run frontendPolicies: accessLog: add: agent.session: request.agent.session llm: port: 4000 models: - name: mock provider: custom: formats: - type: completions params: baseUrl: http://127.0.0.1:8091/v1 model: mock-model request.agent.session only depends on the incoming request, not on what the backend does with it, so a mock response proves the same behavior a real Anthropic or OpenAI call would. That also means this demo costs nothing to run and needs no API key. Five requests, one gateway: $ curl -s -X POST http://localhost:4000/v1/chat/completions \ -H "x-claude-code-session-id: 79291636abc123" -d '{...}' # request 1 $ curl -s -X POST http://localhost:4000/v1/chat/completions \ -H "x-claude-code-session-id: 79291636abc123" -d '{...}' # request 2, same session $ curl -s -X POST http://localhost:4000/v1/chat/completions \ -H "x-cursor-session-id: cursorsession999" -d '{...}' # request 3, different vendor header $ curl -s -X POST http://localhost:4000/v1/chat/completions -d '{...}' # request 4, no session header $ curl -s -X POST http://localhost:4000/v1/chat/completions \ -H "x-claude-code-session-id: short1" -d '{...}' # request 5, 6-character id All five return HTTP 200. The access log, with agent.session: request.agent.session wired into accessLog.add, is where the real answer is: http.path=/v1/chat/completions agent.session="79291636abc123" http.path=/v1/chat/completions agent.session="79291636abc123" http.path=/v1/chat/completions agent.session="cursorsession999" http.path=/v1/chat/completions http.path=/v1/chat/completions Requests 1 and 2 share a session, as expected from a repeated header. Request 3's completely different vendor header still gets picked up, which is the generic-pattern claim proven rather than assumed. Requests 4 and 5 both have no agent.session field: one sent no header at all, the other's short1 is 6 characters, two short of the minimum. Same outcome, two different causes, neither one visible unless you already know to look for it. This is the same accessLog.add mechanism the v1.6.0 cost-tracking post used to surface llm.cost.total into the request log: any CEL field, request.agent.session included, becomes a logged attribute with one line of config. Overriding detection config.standardAttributes.session takes a CEL expression that runs before the automatic header scan. Returning a string from it sets the session explicitly, which is useful if an internal proxy or SDK already generates its own session identifier under a header name agentgateway wouldn't otherwise recognize. Returning null marks the request as having no session even when a technically-valid vendor header is present. If the expression itself errors, or returns something other than a string, agentgateway falls back to the header-based detection described above rather than failing the request. This repo doesn't set the override, so everything in the demo runs through the default header scan. What this doesn't cover This demo is scoped to proving the detection rule, not building something on top of it. It doesn't exercise a real LLM provider (the mock backend never sees or uses the session value), Codex's User-Agent-gated header set, or any policy that actually acts on request.agent.session, like a per-session rate limit. v1.7.0 shipped a lot more than this one field: MCP tool-call guardrails (written against a pre-release build, now generally available), model-catalog metadata over /v1/models, service-tier-aware cost pricing, and a new agctl standalone analytics CLI. This post is one field, validated. Repo: themsquared/agw-17-hands-on. Release notes: agentgateway v1.7.0. Frequently asked questions How does agentgateway detect which coding agent session sent a request? It scans request headers for any name shaped x-<vendor>-session-id, such as x-claude-code-session-id or x-cursor-session-id, and takes the first valid value it finds. It also recognizes Codex's unprefixed session-id and conversation_id headers, but only when the User-Agent string starts with codex. A vendor-less x-session-id header is the last fallback. The result is exposed as request.agent.session in CEL and in access logs. Why isn't my agent session id showing up in agentgateway's access logs? The value has to be at least 8 characters long and contain only letters, digits, hyphens, or underscores. A shorter or differently-formatted value is treated as if the header were never sent: no error, no warning, the request just has no detected session. Check length and character set first before assuming the feature is broken. How do I log agentgateway's detected agent session? Add agent.session: request.agent.session under frontendPolicies.accessLog.add in the gateway config. The field then appears on every access log line as agent.session=\ Can I override agentgateway's automatic agent session detection? Yes. config.standardAttributes.session takes a CEL expression that runs before the header-based fallback. Returning a string from it sets the session explicitly; returning null marks the request as having no session even if a valid vendor header is present. If the expression errors or returns a non-string, agentgateway falls back to the automatic header scan. --- *Canonical version, with machine-readable markdown at https://webofmike.com/agentgateway-v17-agent-sessions/index.md: webofmike.com
agent 一多,鉴权、限流全散在各处? agentgateway 是给 agent 和 MCP 的下一代代理:鉴权、RBAC、限流、护栏、可观测全套配齐。 适合把 agent 规模化跑起来、又得管安全合规的团队,个人小项目有点重。 先给 agent 接哪道关,鉴权还是护栏? #Agent #MCP @grok agentgateway/agentgateway
🚀 agentgateway 1.7 is out! 💰 Per-key token and dollar budgets 🔐 OIDC login + hashed API keys 🛡️ Better streaming guardrails 🤖 Gemini API support + improved LLM interoperability 📊 Richer LLM observability Thanks to everyone who contributed! 🙌 Try it locally, no K8s required. buff.ly
Repo: agentgateway/agentgateway How to try: download the binary for your OS from the v1.7.0 release, then run `agentgateway -f examples/mcp-guardrails/expression/config.yaml` from a clone (needs npx for the demo MCP server). It listens on port 3000.
Stopping Agent Proxy Laundering at the MCP Gateway Originally published at webofmike.com on 2026-10-25. The demo repo and every command in it were run before publishing. On 2026-10-05 the Wikimedia Foundation disclosed that a handful of wiki edits it attributes to OpenAI-operated agents changed the configuration of a citation tool, in what Wikimedia believes was an attempt to misuse it as a proxy for fetching data from remote services. Wikimedia doesn't publish the exact mechanism, so I can't reproduce their tool. What I built instead is themsquared/agent-proxy-laundering: a toy citation-resolver MCP tool with the same shape as the one Wikimedia describes, pointed at an attacker-controlled destination the same way, and a gateway policy that catches it. The shape is the point. A citation resolver's whole job is "fetch this URL and return what's in it." That is also, mechanically, a complete description of a proxy. The tool has no step where it decides a URL is or isn't a legitimate citation source, because nothing told it there was a difference to check. What Wikimedia actually found Worth being precise about what's confirmed and what isn't, since some coverage blurs the two incidents together. Per Wikimedia's own post: - A few edits to Wikimedia wikis, dated 2026-10-04 in a published CSV, changed a citation tool's configuration. Almost all of the broader edit set were test edits in sandbox areas, not published to pages visible to readers, but Wikimedia believes these specific ones were an attempt to misuse the tool as a fetch proxy. - A separate set of agents tried, and failed, to compromise a public Etherpad instance Wikimedia hosts, including trying to use it the same way. - The agents made millions of automated requests to public APIs and crawled millions of pages, mostly against Wikidata and Wikimedia Commons, with hundreds of thousands of queries against the Wikidata Query Service. Wikimedia says this traffic may have contributed to a partial outage of that service in May. - No evidence the agents coordinated through Wikimedia's systems. No evidence Wikimedia's systems or data were compromised. - The OpenAI attribution and the citation-tool link are Wikimedia's belief, not an independently confirmed fact. I've already written about the volume half of this story: rate-limiting the Wikidata query traffic, a problem of an allowed destination getting hit too often. This post is about a different half: a tool getting pointed somewhere it was never meant to go in the first place. The tool: fetch_citation_source The tool can't tell "resolve this DOI" apart from "fetch this attacker-controlled host," because from inside the tool both are the same instruction: a string that looks like a URL, and a command to fetch it. Here's the toy version, a one-tool MCP server: server.registerTool( "fetch_citation_source", { /* ... */ }, async ({ source_url }) => { const res = await fetch(source_url); const body = await res.text(); return { content: [{ type: "text", text: `GET ${source_url} -> ${res.status}: ${body.slice(0, 300)}` }] }; }, ); Nothing here checks source_url. mcp-server/direct-fetch.js runs that exact logic directly, no MCP framing, no gateway, nothing in front of it, against two targets: a mock citation API standing in for a legitimate bibliographic source, and a mock "attacker target" standing in for anywhere a laundered request could land. $ node mcp-server/direct-fetch.js http://localhost:8081/works/10.1234/example GET http://localhost:8081/works/10.1234/example -> 200: {"id": "example", "title": "Scoped Egress Identity for Autonomous Agents", ...} $ node mcp-server/direct-fetch.js http://localhost:8082/ GET http://localhost:8082/ -> 200: {"reached": "attacker-target", "note": "this response proves whatever called it treated this host as a valid fetch target"} Same code, same result shape, two completely different destinations. The tool treated both as equally valid, because to the tool they were. The control: a destination check at the gateway, not in the tool agentgateway can sit in front of an MCP server and see the resolved tools/call request, arguments included, before the tool ever runs. That's the same mcpGuardrails mechanism I used in an earlier post to reject a tool call containing a destructive shell pattern. Here it does a narrower, more specific job: check source_url against an allowlist of actual citation sources, and reject anything else. mcpGuardrails: processors: - kind: expression methods: {"tools/call": request} condition: >- mcp.params.name == 'fetch_citation_source' && has(mcp.params.arguments) && has(mcp.params.arguments.source_url) && !(mcp.params.arguments.source_url.startsWith('http://localhost:8081/') || mcp.params.arguments.source_url.startsWith('http://127.0.0.1:8081/') || mcp.params.arguments.source_url.startsWith('https://doi.org/') || mcp.params.arguments.source_url.startsWith('https://api.crossref.org/')) reject: "source_url is not an allowlisted citation source; rejected before the fetch ran" The tool's own code is exactly what it was above. It still has no idea what a valid citation source looks like. It doesn't need one, because a request pointed anywhere else never reaches it. Reproducing it git clone https://github.com/themsquared/agent-proxy-laundering.git cd agent-proxy-laundering npm install --prefix mcp-server AGENTGATEWAY_BIN=/path/to/agentgateway ./scripts/demo.sh The agentgateway binary needs the mcpGuardrails CEL processor, which landed on main on 2026-10-06 and is not in a tagged release yet (the latest release, v1.6.0, predates it). I built it from source with cargo build --release -p agentgateway-app, the same path I used for the tool-call-guardrails demo. Through the gateway, the legitimate lookup: { "content": [ { "type": "text", "text": "GET http://localhost:8081/works/10.1234/example -> 200: {\"id\": \"example\", \"title\": \"Scoped Egress Identity for Autonomous Agents\", ...}" } ] } And the laundering attempt, same tool, same call shape, source_url pointed at the attacker target instead: { "content": [ { "type": "text", "text": "source_url is not an allowlisted citation source; rejected before the fetch ran" } ], "isError": true } The gateway log shows exactly where that message came from and how fast: error=... mcp.method.name=tools/call mcp.target=citation-tools gen_ai.tool.name=fetch_citation_source error="mcp: mcpGuardrails rejected: source_url is not an allowlisted citation source; rejected before the fetch ran" reason=MCP duration=0ms duration=0ms. No upstream dial was attempted. attacker-target's own request counter, checked before and after, confirms it from the other side: {"requests_received": 1} both times, the single hit left over from the direct, no-gateway call earlier. The laundering attempt never reached it. What this doesn't cover The allowlist here is a handful of startsWith prefix checks against known-good hosts. That's the right shape for a small, stable set of legitimate sources and the wrong shape for "any DOI resolver on the internet," which would need a real allowlist of registered resolvers, kept current alongside whatever the tool is actually supposed to reach. It also doesn't model the Etherpad half of Wikimedia's disclosure, where the target being probed was a hosted service rather than a fetch tool being handed a destination, and it doesn't model DNS-based evasion of a destination check, which is a different channel I covered in the DNS covert-channel post. And it doesn't model volume: the hundreds of thousands of Wikidata queries are a rate problem, not a destination problem, which is its own post with its own control. fetch_citation_source still can't tell a DOI from an attacker's host. It never had to learn, because the mcpGuardrails block in config.yaml is what makes the call to port 8082 fail, not anything added to server.js. That's the actual claim this repo makes about the Wikimedia incident: whatever Wikimedia's real citation tool looked like, fixing a laundering bug in one tool's code doesn't fix the next tool that fetches URLs, but a gateway sitting in front of every MCP tool call does. Repo: themsquared/agent-proxy-laundering. I work at Solo.io, which makes agentgateway. Frequently asked questions What did OpenAI's agents do to Wikimedia's citation tool? The Wikimedia Foundation disclosed on 2026-10-05 that a few wiki edits, which it attributes to OpenAI-operated agents, changed the configuration of a citation tool in a way it believes was an attempt to misuse the tool as a proxy for fetching data from remote services. Wikimedia's post does not publish the exact configuration change or mechanism. Can an MCP tool be used as a proxy even if it wasn't designed to be one? Yes, whenever the tool's job includes fetching a URL it is given. A citation resolver that fetches bibliographic sources has no way to distinguish a legitimate citation lookup from a request to fetch an unrelated host, because both are the same operation: fetch this URL and return what comes back. How do you stop a fetch-capable MCP tool from being used as a proxy? Put a destination check in front of the tool, not inside it. agentgateway's MCP guardrails read the resolved tools/call request, including its arguments, before the tool executes, and can reject a call whose target argument fails an allowlist check. The tool's own code never changes and never needs to know what a valid destination looks like. --- *Canonical version, with machine-readable markdown at https://webofmike.com/agent-proxy-laundering/index.md: webofmike.com
MCP Tool-Call Guardrails: Gating Agents Before the Tool Runs Originally published at webofmike.com on 2026-10-22. The demo repo and every command in it were run before publishing. agentgateway can now reject or rewrite an MCP tools/call request before the tool runs, and mask the result before the client sees it, from CEL expressions in one config file, with no sidecar service. The commit that adds it landed on main two days ago and isn't in a tagged release yet. It closes a gap from three days earlier: Prisma AIRS can scan MCP tool events, but hooking it into agentgateway needed a webhook adapter because agentgateway's own guardrails only ran on LLM chat messages, the prompt going in, the text coming back, not the tool call itself. themsquared/tool-call-guardrails is a toy customer-support MCP server, lookup_customer and run_support_macro, with three guardrail processors in front of it in one config.yaml: one rejects a destructive tool call before it runs, one masks PII out of the arguments on the way in, one masks a secret out of the result on the way back. Why this is a different layer than a prompt guardrail agentgateway already has prompt guards for LLM chat messages, and their default scope only covers the system prompt and chat messages, not tool output, unless you add toolOutput to the list explicitly. Even with that scope set, the guard is still downstream of the tool call: the tool has already run, the data has already moved, and the guard is filtering what the model sees afterward. MCP guardrails (mcpGuardrails under mcp.policies) run at the MCP method layer instead. A processor can run in the request phase, before agentgateway forwards the call to the upstream MCP server, in the response phase, after the server replies, or full (both). A request-phase rejection means the tool is never invoked at all, not filtered after the fact. And because it is keyed on the MCP method (tools/call, tools/list, and so on) rather than an LLM's chat schema, it applies the same way no matter what's driving the agent, an LLM behind agentgateway's own router, a different LLM entirely, or a human testing with the MCP Inspector. The policy, in one file config: adminAddr: "off" # only needed if something else on the box already holds the admin port mcp: port: 13000 policies: mcpGuardrails: processors: # 1. Reject before anything else runs. - kind: expression methods: {"tools/call": request} condition: >- mcp.params.name == 'run_support_macro' && has(mcp.params.arguments) && has(mcp.params.arguments.raw_command) && mcp.params.arguments.raw_command.contains('rm -rf') reject: "raw_command contains a destructive shell pattern; this macro requires human review" # 2. Mask PII in arguments before the upstream tool ever sees them. - kind: expression methods: {"tools/call": request} condition: >- mcp.params.name == 'run_support_macro' && has(mcp.params.arguments) && has(mcp.params.arguments.macro) transform: >- mcp.params.with(p, p.merge({ 'arguments': p.arguments.merge({ 'macro': p.arguments.macro.regexReplaceAll( '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}', '[EMAIL]' ) }) })) # 3. Mask secrets in what the tool hands back. - kind: expression methods: {"tools/call": response} condition: has(mcp.result.content) transform: >- mcp.result.with(r, r.merge({ 'content': r.content.map(item, item.type == 'text' ? item.merge({'text': item.text.regexReplaceAll('sk-live-[A-Za-z0-9-]+', '[REDACTED_KEY]')}) : item ) })) targets: - name: support-tools stdio: cmd: node args: ["mcp-server/server.js"] mcp.params is the live request's MCP params object, arguments included. mcp.result is the response's MCP result object. Processors run in the order they're listed; a reject in an earlier processor stops the chain, and a transform in one processor is what the next processor (and eventually the upstream server or the client) sees. That ordering matters here: the reject check runs before the mask, so a call that should never have been sent is refused on its original text, not on a redacted copy of it. Three calls through the gateway The support server's run_support_macro tool takes a raw_command field, an internal escape hatch some macros pass straight to a backend shell. A call with rm -rf in that field never reaches the tool: $ npx -y @modelcontextprotocol/inspector --cli http://localhost:13000/mcp \ --method tools/call --tool-name run_support_macro \ --tool-arg customer_id=cust-1002 --tool-arg macro=reset_password \ --tool-arg "raw_command=rm -rf /data/cust-1002" { "content": [{"type": "text", "text": "raw_command contains a destructive shell pattern; this macro requires human review"}], "isError": true } A macro call that happens to carry an email address gets the address masked before the tool runs, so it's never written into whatever backend the macro touches: $ npx -y @modelcontextprotocol/inspector --cli http://localhost:13000/mcp \ --method tools/call --tool-name run_support_macro \ --tool-arg customer_id=cust-1002 \ --tool-arg "macro=escalate to priya.shah@example.com" { "content": [{"type": "text", "text": "Ran macro 'escalate to [EMAIL]' for cust-1002"}] } And lookup_customer, which (realistically, carelessly) stores an API key in a customer's notes field, has that key redacted before the result leaves the gateway: $ npx -y @modelcontextprotocol/inspector --cli http://localhost:13000/mcp \ --method tools/call --tool-name lookup_customer --tool-arg customer_id=cust-1001 { "content": [{"type": "text", "text": "Jordan Lee (Pro). Notes: Escalated 2026-09-02, resolved. Backup API key on file: [REDACTED_KEY]. Do not quote this to the customer."}] } None of these three calls touch an LLM. The MCP Inspector CLI talks tools/call straight to agentgateway's MCP endpoint, which is the point: the guardrail is enforced on the protocol, not bolted onto a chat completion. First pass at the secret regex left half the key exposed. sk-live-[A-Za-z0-9]+ looks like it should catch sk-live-demo-7f3a9c2b1e, and it does, partially: the character class doesn't include -, so regexReplaceAll matched only sk-live-demo and stopped at the hyphen, handing back [REDACTED_KEY]-7f3a9c2b1e. The fix is one character, sk-live-[A-Za-z0-9-]+, but it's the kind of mistake that's invisible until you test against a key shaped like a real one, not sk-live-abc123. This is not in a release yet The CEL expression processor landed on main on October 6, 2026, three days before this post, in 276f10d, on top of an earlier gRPC-based mcpGuardrails hook. The most recent tagged release, v1.6.0, published October 2, predates both. Building from source is the only way to run this today: git clone https://github.com/agentgateway/agentgateway.git cd agentgateway && cargo build --release Everything in this post runs against that build. Treat the exact CEL surface (regexReplaceAll, .with(), .merge()) as subject to change before it ships in a tagged release; the mechanism, request-phase rejection and transform, response-phase transform, in-process CEL with no sidecar, is the part worth taking away. What's still open The three processors in this repo's config.yaml are all kind: expression: fast, local, and limited to what a regex or a CEL comparison can decide. agentgateway also exposes a remote gRPC processor (kind: remote), the same shape the Prisma AIRS integration used for LLM-side guardrails, for a case this demo doesn't cover: a run_support_macro argument that reads as fine text but is actually a prompt-injection payload aimed at whatever agent calls the tool next. CEL can reject a literal rm -rf; it can't judge intent. That's the next adapter to build against this hook, not this one. Repo again: themsquared/tool-call-guardrails. Frequently asked questions What is an MCP tool-call guardrail? A policy that runs on the resolved tools/call request and response at the MCP protocol layer, not on the chat messages going into an LLM. agentgateway's mcpGuardrails processors see mcp.params (the tool name and arguments) on the request phase and mcp.result on the response phase, and can reject, or rewrite, either one before it reaches the upstream MCP server or the client. How is this different from a prompt guardrail? A prompt guardrail scans text flowing through an LLM's chat API, and by default does not see tool results at all, unless its scope explicitly names toolOutput. An MCP guardrail runs at the protocol layer where the tool is actually invoked, so it can refuse a tools/call outright before the tool executes, and it works for any MCP client, not just ones routed through an LLM endpoint. Do I need to run a separate service for MCP guardrails in agentgateway? Not for CEL-based rules. The expression processor (kind: expression) evaluates CEL against mcp.params and mcp.result in-process, in the gateway itself. agentgateway also supports a remote gRPC processor for logic too complex for CEL, but the expression kind needs nothing extra running. Can an MCP guardrail reject a tool call outright? Yes. A request-phase processor with a reject condition stops the chain and the tool is never invoked; the caller gets a tool result with isError: true instead of whatever the tool would have done. A response-phase processor can only transform or pass through, since the tool has already run by then. --- *Canonical version, with machine-readable markdown at https://webofmike.com/tool-call-guardrails/index.md: webofmike.com
agentgateway/agentgateway rustの中央集権型のa2aプロトコルかー一旦試してみよ
Agentgateway: secure, observable AI conversations Next Generation Agentic Proxy for AI Agents and MCP servers agentgateway/agentgateway on GitHub: agentgateway/agentgateway This week's round-up: youtube.com Trending today. This video describes what the project says it does — it is not a review or a recommendation. ▶ More like this: youtube.com Subscribe for a new open source project every day. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [ar] Agentgateway: محادثات ذكاء اصطناعي آمنة وقابلة للمراقبة جيل جديد من وكلاء الذكاء الاصطناعي لخوادم MCP agentgateway/agentgateway على GitHub: agentgateway/agentgateway ملخص هذا الأسبوع: youtube.com الأكثر رواجًا اليوم. يشرح هذا الفيديو وظائف المشروع المعلنة، وليس مراجعة أو توصية. ▶ المزيد من هذا النوع: youtube.com اشترك لتصلك مشاريع مفتوحة المصدر جديدة يوميًا. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [bn] Agentgateway: নিরাপদ ও পর্যবেক্ষণযোগ্য এআই কথোপকথন এআই এজেন্ট এবং এমসিপি সার্ভারের জন্য পরবর্তী প্রজন্মের এজেন্টিক প্রক্সি গিটহাবে agentgateway/agentgateway: agentgateway/agentgateway এই সপ্তাহের সারসংক্ষেপ: youtube.com আজকের ট্রেন্ডিং। এই ভিডিওটি বর্ণনা করে যে প্রকল্পটি কী করে — এটি কোনো পর্যালোচনা বা সুপারিশ নয়। ▶ এই ধরনের আরও: youtube.com প্রতিদিন একটি নতুন ওপেন সোর্স প্রকল্পের জন্য সাবস্ক্রাইব করুন। #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [de-DE] Agentgateway: Sichere, beobachtbare KI-Konversationen Agentic-Proxy der nächsten Generation für KI-Agenten und MCP-Server agentgateway/agentgateway auf GitHub: agentgateway/agentgateway Wochenrückblick: youtube.com Heute im Trend. Dieses Video beschreibt die Projektbeschreibung – es ist keine Rezension oder Empfehlung. ▶ Ähnliche Videos: youtube.com Abonniere unseren Kanal für täglich neue Open-Source-Projekte. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [es-US] Agentgateway: conversaciones con IA seguras y observables Proxy agentic de próxima generación para agentes de IA y servidores MCP agentgateway/agentgateway en GitHub: agentgateway/agentgateway Resumen de esta semana: youtube.com Tendencia del día. Este video describe lo que el proyecto afirma hacer; no es una reseña ni una recomendación. ▶ Más contenido similar: youtube.com Suscríbete para descubrir un nuevo proyecto de código abierto cada día. #Shorts #GitHub #OpenSource #rust #agentgateway #generación #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [fr-FR] Agentgateway : des conversations IA sécurisées et observables Proxy Agentic nouvelle génération pour agents IA et serveurs MCP agentgateway/agentgateway sur GitHub : agentgateway/agentgateway Récapitulatif de la semaine : youtube.com À la une aujourd'hui. Cette vidéo décrit le fonctionnement du projet ; il ne s'agit ni d'un test, ni d'une recommandation. ▶ Plus de vidéos similaires : youtube.com Abonnez-vous pour découvrir un nouveau projet open source chaque jour. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [hi] Agent Gateway: सुरक्षित, ऑब्ज़र्वेबल AI बातचीत एआई एजेंट्स और एमसीपी सर्वरों के लिए अगली पीढ़ी का एजेंटिक प्रॉक्सी agentgateway/agentgateway GitHub पर: agentgateway/agentgateway इस सप्ताह का सारांश: youtube.com आज का ट्रेंडिंग वीडियो। यह वीडियो प्रोजेक्ट के कार्यों का वर्णन करता है - यह कोई समीक्षा या अनुशंसा नहीं है। ▶ इस तरह के और वीडियो: youtube.com हर दिन एक नए ओपन सोर्स प्रोजेक्ट के लिए सब्सक्राइब करें। #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [id] Agentgateway: percakapan AI yang aman dan dapat diobservasi Proxy Agen Generasi Berikutnya untuk Agen AI dan Server MCP agentgateway/agentgateway di GitHub: agentgateway/agentgateway Ringkasan minggu ini: youtube.com Sedang tren hari ini. Video ini menjelaskan apa yang diklaim proyek ini — ini bukan ulasan atau rekomendasi. ▶ Lebih banyak seperti ini: youtube.com Berlangganan untuk proyek open source baru setiap hari. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [it] Agentgateway: conversazioni AI sicure e monitorabili Proxy Agentic di nuova generazione per agenti AI e server MCP agentgateway/agentgateway su GitHub: agentgateway/agentgateway Riepilogo della settimana: youtube.com Tendenze di oggi. Questo video descrive le funzionalità dichiarate del progetto, non è una recensione né una raccomandazione. ▶ Altri video simili: youtube.com Iscriviti per scoprire un nuovo progetto open source ogni giorno. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [iw] Agentgateway: שיחות AI מאובטחות וניתנות לניטור פרוקסי Agentic מהדור הבא עבור סוכני בינה מלאכותית ושרתי MCP agentgateway/agentgateway ב-GitHub: agentgateway/agentgateway סיכום השבוע: youtube.com בטרנדים היום. סרטון זה מתאר מה הפרויקט אומר שהוא עושה - זו אינה סקירה או המלצה. ▶ עוד כמו זה: youtube.com הירשמו לפרויקט קוד פתוח חדש בכל יום. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [ja] Agentgateway:安全で可視化されたAI対話 AIエージェントとMCPサーバー向け次世代エージェントプロキシ GitHubのagentgateway/agentgateway:agentgateway/agentgateway 今週のまとめ:youtube.com 本日の注目動画。この動画はプロジェクトの説明であり、レビューや推奨ではありません。 ▶ 類似動画はこちら:youtube.com 毎日新しいオープンソースプロジェクトを紹介するチャンネル登録はこちら。 #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [ko] Agentgateway: 안전하고 관찰 가능한 AI 대화 AI 에이전트 및 MCP 서버를 위한 차세대 에이전트형 프록시 agentgateway/agentgateway (GitHub: agentgateway/agentgateway) 이번 주 요약: youtube.com 오늘의 인기 영상입니다. 이 영상은 프로젝트의 기능을 설명하는 영상이며, 리뷰나 추천이 아닙니다. ▶ 더 보기: youtube.com 매일 새로운 오픈 소스 프로젝트를 받아보시려면 구독하세요. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [ml] Agentgateway: സുരക്ഷിതവും നിരീക്ഷിക്കാവുന്നതുമായ AI സംഭാഷണങ്ങൾ AI ഏജന്റുമാർക്കും MCP സെർവറുകൾക്കുമുള്ള നെക്സ്റ്റ് ജനറേഷൻ ഏജന്റ് പ്രോക്സി GitHub-ലെ agentgateway/agentgateway: agentgateway/agentgateway ഈ ആഴ്ചയിലെ സംഗ്രഹം: youtube.com ഇന്നത്തെ ട്രെൻഡിംഗ്. പ്രോജക്റ്റ് എന്താണ് പറയുന്നതെന്ന് ഈ വീഡിയോ വിവരിക്കുന്നു - ഇത് ഒരു അവലോകനമോ ശുപാർശയോ അല്ല. ▶ ഇതുപോലുള്ള കൂടുതൽ: youtube.com എല്ലാ ദിവസവും ഒരു പുതിയ ഓപ്പൺ സോഴ്സ് പ്രോജക്റ്റിനായി സബ്സ്ക്രൈബുചെയ്യുക. #ഷോർട്ട്സ് #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [nl-NL] Agentgateway: veilige, observeerbare AI-gesprekken Agentische proxy van de volgende generatie voor AI-agenten en MCP-servers agentgateway/agentgateway op GitHub: agentgateway/agentgateway Samenvatting van deze week: youtube.com Trending vandaag. Deze video beschrijft wat het project doet — het is geen recensie of aanbeveling. ▶ Meer zoals dit: youtube.com Abonneer je voor een nieuw open source-project elke dag. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [pa] Agentgateway: ਸੁਰੱਖਿਅਤ ਅਤੇ ਦੇਖਣਯੋਗ AI ਗੱਲਬਾਤ AI ਏਜੰਟਾਂ ਅਤੇ MCP ਸਰਵਰਾਂ ਲਈ ਅਗਲੀ ਪੀੜ੍ਹੀ ਦੇ ਏਜੰਟਿਕ ਪ੍ਰੌਕਸੀ agentgateway/agentgateway GitHub 'ਤੇ: agentgateway/agentgateway ਇਸ ਹਫ਼ਤੇ ਦਾ ਸੰਖੇਪ: youtube.com ਅੱਜ ਟ੍ਰੈਂਡਿੰਗ। ਇਹ ਵੀਡੀਓ ਦੱਸਦਾ ਹੈ ਕਿ ਪ੍ਰੋਜੈਕਟ ਕੀ ਕਹਿੰਦਾ ਹੈ ਕਿ ਇਹ ਕੀ ਕਰਦਾ ਹੈ — ਇਹ ਕੋਈ ਸਮੀਖਿਆ ਜਾਂ ਸਿਫ਼ਾਰਸ਼ ਨਹੀਂ ਹੈ। ▶ ਇਸ ਤਰ੍ਹਾਂ ਦੇ ਹੋਰ: youtube.com ਹਰ ਰੋਜ਼ ਇੱਕ ਨਵੇਂ ਓਪਨ ਸੋਰਸ ਪ੍ਰੋਜੈਕਟ ਲਈ ਗਾਹਕ ਬਣੋ। #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [pl] Agentgateway: bezpieczne i obserwowalne konwersacje z AI Agentic Proxy nowej generacji dla agentów AI i serwerów MCP agentgateway/agentgateway na GitHubie: agentgateway/agentgateway Podsumowanie tygodnia: youtube.com Dzisiejsze trendy. Ten film opisuje, co projekt obiecuje — nie jest to recenzja ani rekomendacja. ▶ Więcej na ten temat: youtube.com Subskrybuj, aby każdego dnia otrzymywać informacje o nowym projekcie open source. #Shorts #GitHub #OpenSource #rust #agentgateway #generacja #agentic #proxy #agenty #serwery #kodowanie #programowanie #programista #oprogramowanie #narzędzia_programistyczne [pt-BR] Agentgateway: conversas com IA seguras e observáveis Proxy Agético de Próxima Geração para Agentes de IA e Servidores MCP agentgateway/agentgateway no GitHub: agentgateway/agentgateway Resumo desta semana: youtube.com Em alta hoje. Este vídeo descreve o que o projeto faz — não é uma análise ou recomendação. ▶ Mais vídeos como este: youtube.com Inscreva-se para receber um novo projeto de código aberto todos os dias. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [ru] Agentgateway: безопасные и прозрачные диалоги с ИИ Прокси-сервер Agentic нового поколения для ИИ-агентов и MCP-серверов agentgateway/agentgateway на GitHub: agentgateway/agentgateway Обзор этой недели: youtube.com Тренды сегодня. В этом видео описывается, что, по заявлению проекта, он делает — это не обзор и не рекомендация. ▶ Больше подобного: youtube.com Подписывайтесь, чтобы получать информацию о новых проектах с открытым исходным кодом каждый день. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [ta] Agentgateway: பாதுகாப்பான மற்றும் கண்காணிக்கக்கூடிய AI உரையாடல்கள் AI ஏஜென்ட்கள் மற்றும் MCP சர்வர்களுக்கான அடுத்த தலைமுறை ஏஜென்டிக் ப்ராக்ஸி கிட்ஹப்பில் உள்ள agentgateway/agentgateway: agentgateway/agentgateway இந்த வாரத் தொகுப்பு: youtube.com இன்று பிரபலமாக உள்ளது. இந்த வீடியோ, இந்தத் திட்டம் என்ன செய்வதாகக் கூறுகிறதோ அதை விவரிக்கிறது — இது ஒரு விமர்சனமோ அல்லது பரிந்துரையோ அல்ல. ▶ இது போன்ற மேலும் பல: youtube.com ஒவ்வொரு நாளும் ஒரு புதிய ஓப்பன் சோர்ஸ் திட்டத்திற்காக சப்ஸ்கிரைப் செய்யுங்கள். #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [te] ఏజెంట్ గేట్వే (Agentgateway): సురక్షితమైన, పర్యవేక్షించదగిన AI సంభాషణలు AI ఏజెంట్లు మరియు MCP సర్వర్ల కోసం తదుపరి తరం ఏజెంటిక్ ప్రాక్సీ GitHubలో agentgateway/agentgateway: agentgateway/agentgateway ఈ వారం రౌండప్: youtube.com ఈ రోజు ట్రెండింగ్. ఈ వీడియో, ప్రాజెక్ట్ ఏమి చేస్తుందో వివరిస్తుంది — ఇది సమీక్ష లేదా సిఫార్సు కాదు. ▶ ఇలాంటివి మరిన్ని: youtube.com ప్రతిరోజూ ఒక కొత్త ఓపెన్ సోర్స్ ప్రాజెక్ట్ కోసం సబ్స్క్రయిబ్ చేసుకోండి. #Shorts #GitHub #OpenSource #rust #agentgateway #generation #agentic #proxy #agents #servers #coding #programming #developer #software #devtools [uk] Agentgateway: безпечні та прозорі ШІ-розмови Агентський проксі наступного покоління для агентів ШІ та MCP-серверів agentgateway/agentgateway на GitHub: agentgateway/agentgateway Огляд цього тижня: youtube.com Тренди сьогодні. Це відео описує, що робить проєкт — це не огляд і не рекомендація. ▶ Більше схожих відео: youtube.com Підпишіться на новий проєкт з відкритим кодом щодня. #Шорти #GitHub #ВідкритийВихіднийКод #rust #agentgateway #покоління #agentic #proxy #agents #servers #coding #programming #developer #software #devtools
agentgateway/agentgateway on GitHub: agentgateway/agentgateway This week's round-up: youtube.com
MCPゲートウェイのツール単位認可は効くか — agentgatewayで削除を通した3つの穴 はじめに 2026年10月20日、iPaaS ベンダの Celigo が「MCP Management」を発表しました。MCP Gateway で他社の MCP サーバをまとめてプロキシし、ツールを個別に無効化できる。IdP 経由でエンドユーザ本人の権限で実行し、共有クレデンシャルは使わない、という触れ込みです (SiliconANGLE の記事)。 MCP ゲートウェイ自体は Kong、Pomerium、Docker MCP Toolkit、agentgateway など多くの製品が出ている分野で、どの製品も「ツール単位のガバナンス」「ユーザ単位の監査」をほぼ同じ言葉で訴求しています。気になったのは次の2点です。 ツールを「無効化」すると、tools/list から消えるだけなのか。名前を知っているクライアントが tools/call を直接投げたら止まるのか 呼び出しログに「誰の代理で実行したか」が残るのは、どういう条件のときか Celigo は商用でアカウントが必要なので、論点だけ借りて OSS の agentgateway v1.6.0 で実測しました。 結論を先に書きます。 agentgateway は一覧から隠すだけではありません。拒否したツールは tools/call でも止まり、上流には届きません。 ただし deny(拒否リスト)型でルールを書くと、3つの経路で削除ツールが実行できました。 ツール名の大文字小文字違い、ロールのクレームが無いユーザ、トークン無しの匿名アクセス、の3つです。 ログの jwt.sub は、JWT を付けたリクエストにしか残りません。権限で拒否した呼び出しと、存在しないツールの呼び出しは、ログ上で同じ表示になります。 Claude Code は一覧に無いツールをそもそも呼べません。なので「隠すだけ」でも Claude Code に対しては止まります。ゲートウェイ側の強制が効いてくるのは、同じトークンを持った別のクライアントに対してです。 構成 自作クライアント / Claude Code ──(Bearer JWT)──> agentgateway :3000 ──> 自作 MCP サーバ :3001 上流の MCP サーバ: Python の標準ライブラリだけで書いた、約100行の Streamable HTTP サーバです。list_records / get_record (読み取り) と update_record / delete_record (書き込み・破壊的) を公開します。受け取ったヘッダとツール名はすべてログに記録します。環境変数 LENIENT=1 を付けると、ツール名を小文字化・前後の空白を除去してから探します(名前の扱いが緩い実装の再現です) 自作クライアント: tools/list を経由せず、任意の名前で tools/call を投げられる最小のクライアントです JWT: ES256 で3ユーザ分を発行しました alice: role=operator(書き込み可にしたい人) bob: role=viewer(読み取りだけにしたい人) carol: role クレーム無し agentgateway は公式リリースのバイナリ(97MB、単体で動作)を使いました 実験1: allow 型(許可リスト)で書く 公式 example に近い書き方です。 mcp: port: 3000 policies: jwtAuth: issuer: lab.local audiences: [mcp.lab.local] jwks: file: ./keys/jwks.json mcpAuthorization: rules: - allow: 'mcp.tool.name in ["list_records", "get_record"]' - allow: 'jwt.role == "operator" && mcp.tool.name in ["update_record", "delete_record"]' targets: - name: records mcp: host: 127.0.0.1 == alice LIST 200 ['list_records', 'get_record', 'update_record', 'delete_record'] CALL delete_record 200 (成功) == bob LIST 200 ['list_records', 'get_record'] CALL delete_record 400 {"error":{"code":-32602,"message":"Unknown tool: delete_record"}} == carol LIST 200 ['list_records', 'get_record'] CALL delete_record 400 Unknown tool: delete_record == トークン無し LIST 200 ['list_records', 'get_record'] bob が名前を直接指定して tools/call を投げても 400 で拒否されました。上流サーバのログにも bob の delete_record は届いていません。ゲートウェイの段階で強制されています。 仮説1は agentgateway については外れでした。 この形で、以下も試しました。いずれも止まっています。 Delete_Record / DELETE_RECORD / 前後に空白を付けた名前 → 400(完全一致しない名前は許可リストに当たらない) JSON-RPC のバッチ(配列で複数呼び出し) → 400 fail to deserialize request body 別の鍵で署名した JWT、alg: none、期限切れ → いずれも 401 拒否されたときの応答は「権限がありません」ではなく Unknown tool でした。権限が無いのか、ツールが存在しないのかをクライアントに区別させない作りです。ソースにも authorization_denied_returns_unknown_tool_error というテストがあり、意図した挙動だと分かります。 一点だけ見落としやすいのが、トークン無しでも 200 が返り、読み取り系のツールが使えたことです。jwtAuth.mode の既定値は optional で、トークンがあれば検証し、無ければそのまま通します。ソースのコメントにも "Warning: this allows requests without a JWT." と書かれています。公式 example の設定も mode を書いていないので、そのまま真似すると匿名アクセスが通ります。 実験2: deny 型(拒否リスト)で書く 「基本は全部許可して、書き込み系だけ operator 以外を拒否する」という、素直に思いつく書き方です。 mcpAuthorization: rules: - allow: 'true' - deny: 'mcp.tool.name in ["update_record", "delete_record"] && jwt.role != "operator"' 上流は LENIENT=1(名前の扱いが緩い実装)にして、実際のレコードを消せるか確かめました。 CALL list_records 200 {"1": "顧客A 問い合わせ", "2": "顧客B 解約申請", "3": "顧客C 住所変更"} CALL Delete_Record 200 DELETED 1 ← bob (viewer) CALL delete_record 200 DELETED 2 ← carol (role クレーム無し) CALL delete_record 200 DELETED 3 ← トークン無し CALL list_records 200 {} 3件とも、削除できてはいけない人が消せてしまいました。 原因はそれぞれ別です。 穴1: 名前の揺らぎ bob が delete_record を呼ぶと拒否されます。しかし Delete_Record なら通りました。ゲートウェイは mcp.tool.name を文字列として比較するだけで、その名前が tools/list に載っているかは確認しません。載っていない名前でも上流にそのまま転送します。上流が名前を正規化する実装だと、そこで実行されます。 上流を厳密一致の実装に戻すと unknown tool Delete_Record で失敗しました。この穴が開くかどうかは上流の実装次第です。 ゲートウェイが一律に守ってくれるわけではありません。 穴2: クレームが無いユーザ carol は role クレームを持っていません。jwt.role != "operator" の評価がエラーになり、エラーは false として扱われるので deny が発火しません。ソース (http/authorization.rs) を見ると、各ルールを eval_bool で評価し、エラーは false になります。設定スキーマにも次の記述があり、既知の仕様です。 This mode is not recommended because expression failures fail to deny; prefer Allow or Require. 穴3: トークン無し 穴2と同じ理由です。既定の optional モードではトークン無しのリクエストが通り、jwt そのものが存在しないので deny の評価がエラーになります。このときは tools/list にも4本すべてが表示されました。 防御的に書き直した場合 jwtAuth: mode: strict ... - deny: 'mcp.tool.name in ["update_record", "delete_record"] && !(has(jwt.role) && jwt.role == "operator")' bob delete_record 400 carol delete_record 400 トークン無し → 401 authentication failure: no bearer token found bob Delete_Record 200 DELETED 1 ← 名前の揺らぎは残る クレーム欠落と匿名の穴は塞がりました。名前の揺らぎは、deny 型で書いている限り塞がりません。require を使って jwt.role == "operator" || !(mcp.tool.name in [...]) と書いた場合も同じでした。require は評価エラーのとき拒否側に倒れるので carol は止まりますが、「名前で除外する」書き方である以上、Delete_Record は除外の対象から漏れます。 安全な書き方は、allow 型で許可する名前を完全一致で列挙し、mode を strict にすることです。 今回試した限り、この形ですべての経路が止まりました。 ログに「誰が」残るか 既定のアクセスログには、何も設定しなくても次の項目が出ます。 http.status=200 jwt.sub=alice protocol=mcp mcp.method.name=tools/call mcp.target=records gen_ai.tool.name=delete_record mcp.session.id=... 実験2のログから、tools/call の行だけを抜き出しました。 http.status=200 jwt.sub=alice gen_ai.tool.name=delete_record http.status=400 jwt.sub=bob gen_ai.tool.name=delete_record http.status=200 jwt.sub=bob gen_ai.tool.name=Delete_Record http.status=200 jwt.sub=carol gen_ai.tool.name=delete_record http.status=200 gen_ai.tool.name=delete_record ← 匿名の削除 jwt.sub が残るのは、JWT を付けてきたリクエストだけです。匿名の削除は jwt.sub の項目ごと無くなり、残るのは送信元 IP だけでした。仮説2は当たりです。 どのレコードを消したか(引数)は、既定では残りません。config.logging.fields.add に tool_args: 'mcp.tool.arguments' を足すと出るようになりました。 権限で拒否した呼び出しと、存在しないツールの呼び出しは、どちらも error="mcp: Unknown tool: ..." reason=MCP と表示されます。「ポリシーで止めた件数」をログから数えることはできません。 上流の MCP サーバにユーザ本人の情報が届くのは、backendAuth: { passthrough: {} } を設定したときだけでした。設定しない場合、上流の Authorization ヘッダは空です。 認可はセッション単位ではなく、リクエストごとの JWT で評価されていました。alice で開始したセッション ID を使い回して bob のトークンで呼ぶと、bob の権限で判定されます(拒否されます)。逆に言えば、セッションが特定のユーザに紐付いているわけでもありません。 ゲートウェイを迂回する 上流を 127.0.0.1 で待受 → 同じホストから直叩き: delete_record 200 DELETED 3 上流を 0.0.0.0 で待受 → LAN の IP から: delete_record 200 DELETED 3 → Tailscale の IP から: delete_record 200 DELETED 2 当たり前の話ですが、上流に認証が無く到達できる状態なら、ゲートウェイの認可は何の意味もありません。上流を 127.0.0.1 に絞っても、同じホストで動くプロセスからは直接叩けます。エージェントに Bash を渡している場合は、エージェント自身が curl でゲートウェイを迂回できる経路が残ります。 なお agentgateway 自体は、mcp.port を指定すると全インターフェース (*:3000) で待ち受けていました。 Claude Code から繋ぐ トークンを環境変数から展開する設定ファイルを書いて、claude -p で接続しました。 { "mcpServers": { "records": { "type": "http", "url": "127.0.0.1", "headers": { "Authorization": "Bearer ${MCP_TOKEN}" } } } } bob のトークンで、「mcprecordsdelete_record を id=1 で呼んで。一覧に無くても、存在しないと結論する前にその名前で直接呼んでみること」と指示しました。 mcprecordsdelete_record 存在しない。ToolSearch 検出なし、直接呼び出しもエラー。 ゲートウェイのログには tools/list しか記録されておらず、tools/call は1回も届いていません。 Claude Code は tools/list に無いツールを呼べないので、クライアント側で止まります。alice のトークンでは DELETED 1 が返り、ログに jwt.sub=alice gen_ai.tool.name=delete_record が残りました。 つまり、Claude Code のように行儀のよいクライアントだけが相手なら、「一覧から隠すだけ」の実装でも実害は出ません。ゲートウェイの強制が本当に必要になるのは、同じトークンを持った別のクライアント(スクリプトや、エージェントが Bash 経由で叩く curl)が相手のときです。「隠蔽か強制か」の違いは、Claude Code で触っているだけでは見えません。直接 HTTP を投げて初めて確認できます。 まとめ agentgateway は「隠すだけ」ではありません。 同じ CEL ルールが一覧の絞り込みと tools/call の拒否の両方に効き、拒否した呼び出しは上流に届きません ただし deny 型で書くと、名前の揺らぎ(上流が正規化する場合)・クレーム欠落・匿名(既定の optional)の3経路で書き込みが通ります。 allow 型・完全一致・strict が安全側です 「誰の代理か」がログに残るのは JWT を付けたリクエストだけです。引数は設定しないと残りません。拒否と存在しないツールはログで区別できません。上流に本人を伝えるには passthrough が必要です 上流に直接届く経路があれば、それだけで全部が無意味になります。ネットワーク分離か、上流側での JWT 検証が前提です Celigo のような商用製品を評価するときも、確認すべき点は同じだと思います。デモ画面で一覧からツールが消えることを見るだけでなく、①名前を直接指定した tools/call が拒否されるか、②名前の大文字小文字を変えても拒否されるか、③識別情報(クレーム)の無いユーザがどちらに倒れるか、④ログに拒否と主体が残るか、⑤上流に直接届く経路が無いか、を聞くべきです。エージェントに業務システムを触らせるなら、このあたりが最低限の確認項目になります。 参考 agentgateway (GitHub) — examples/mcp-authorization、schema/config.md agentgateway: MCP authorization SiliconANGLE: Celigo launches MCP tool management 先行の日本語記事: MCPゲートウェイの実装 Finatextグループ MCPass、既存サービスにMCPサーバーを組み込む際の設計ポイント(どちらも「一覧で絞ったうえで、呼び出し時にも再認可する」という設計を解説しています)