mcp-guarded-tools
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-guarded-toolsSearch for a tool that refunds orders, then describe and invoke it for order 123"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-guarded-tools
An MCP server that treats a large tool surface as a governance problem: 43 domain tools sit behind 4 always-on meta-tools, and every call passes a composable chain of guardrails — scope, budgets, rate limits, schema validation, human-in-the-loop confirmation for writes, PII redaction, and an append-only audit log.
Most public MCP servers are thin API wrappers: they register every tool up front and execute whatever the model asks. That works at 5 tools. At 40+, two problems appear that this repo is built to explore:
Context cost — every tool schema is paid for on every request, whether used or not.
Blast radius — an agent loop that can call
refund_orderorpurge_tenant_datadirectly has no natural place to put a budget, an approval step, or a review trail.
Quickstart (60 seconds, zero credentials)
npm install
npm run demoNo API keys, no network calls, no database. The demo spawns the real server as a child process, drives it over the real stdio MCP transport, and prints the transcript below.
npm run build # tsc, strict mode
npm test # vitest — 62 tests, all offline
npm run lint # eslint + prettier
npm run tokens # the context-cost measurementRequires Node 20+.
Related MCP server: Efficient GitLab MCP
What the model actually sees
Tools exposed over MCP: 4
- search_tools find tools by keyword
- describe_tool get one tool's full JSON Schema
- confirm_action mint a single-use token for a mutating tool
- invoke_tool run a tool by name
Domain tools hidden behind them: 43The model discovers tools by searching, not by receiving 43 schemas up front.
Measured context cost
Both surfaces expose the same 43 tools. The difference is what lands in the context window before the conversation starts.
Surface | Tokens |
A. All 43 schemas dumped (conventional | 2,913 |
B. Tool-search facade (4 meta-tools) | 486 |
Difference | 2,427 fewer tokens (83.3% smaller) |
How this was counted. Each tool is serialised into the exact MCP Tool shape ({name, description, inputSchema}), the array is JSON.stringify'd with no pretty-printing, and the string is tokenized with gpt-tokenizer using the o200k_base encoding. Surface B is measured from the live tools/list response of the running server in that same run, not from a hand-written copy. Counts cover the tool-definition payload only — protocol envelope and system prompt are identical for both and excluded.
Reproduce:
npm run demo # prints surface B measured live -> 486
npm run tokens # standalone; uses static schema copies -> 477The two commands differ by 9 tokens (486 vs 477) because npm run tokens measures a static copy of the meta-tool definitions while npm run demo measures what the server actually emitted. 486 is the honest number — it is what a client really receives. The gap is left visible rather than reconciled away.
This is not a free win, and the break-even is measured too. Surface B moves cost from startup to run time: discovering a tool costs a search_tools reply (178 tokens for a 5-result response) and, when the schema is needed, a describe_tool reply (88 tokens for orders.refund_order). At ~266 tokens per discovery cycle, the 2,427-token saving is repaid after roughly 10 search+describe cycles in a single session. Below that, the facade wins; above it, dumping every schema would have been cheaper. Sessions that touch a handful of tools out of a large catalogue are the case this design targets — a session that methodically uses most of the catalogue is not.
Scaling note: surface B is fixed at 4 schemas regardless of catalogue size, so the gap widens as tools are added. It also narrows to nothing if the catalogue is small — at ~10 tools this indirection is not worth it.
Architecture
flowchart TD
C[MCP client] -->|stdio JSON-RPC| M{{4 meta-tools}}
M --> S[search_tools]
M --> D[describe_tool]
M --> K[confirm_action]
M --> I[invoke_tool]
S -.-> R[(Tool registry<br/>43 tools)]
D -.-> R
K -->|mints single-use<br/>args-bound token| T[(Session state)]
I --> G1
subgraph GC [Guardrail chain — first denial short-circuits]
direction TB
G1[1 · scope<br/>allow-list, default-deny] --> G2[2 · budget<br/>requests + tokens]
G2 --> G3[3 · rate limit<br/>sliding window per tool]
G3 --> G4[4 · validation<br/>Zod parse + coerce]
G4 --> G5[5 · confirmation<br/>required for mutating]
end
G5 -->|allowed| H[Tool handler<br/>tenant-filtered data]
H --> RD[PII redaction<br/>in + out]
RD --> C
G1 & G2 & G3 & G4 & G5 -.->|denied| A
RD -.-> A[(Audit log<br/>append-only JSONL)]
T -.-> G5Chain order is cheap-before-expensive, and confirmation is deliberately last so it binds to validated arguments — otherwise {"qty": "5"} and {"qty": 5} would hash differently and describe the same call.
Every outcome, allowed or denied, is written to the audit log with arguments redacted.
Demo transcript
Produced by npm run demo on Node 24.18.0. Reproduced verbatim; only long JSON bodies are elided where the demo itself truncates them.
--- 1. What the model actually sees: tools/list ---
Tools exposed over MCP: 4
- search_tools: Find domain tools by keyword. Returns name, domain, one-line s...
- describe_tool: Return the full description and JSON Schema for one tool disco...
- confirm_action: Issue a single-use, time-limited confirmation token for a muta...
- invoke_tool: Run a domain tool by name. Arguments are validated against the...
Domain tools hidden behind them: 43. The model never receives these 43 schemas up front.
--- 2. Tool-search discovery (instead of prompt-dumping) ---
OK search_tools({query:"money back to a buyer"})
{
"query": "money back to a buyer",
"total_registered": 43,
"returned": 1,
"results": [
{
"name": "orders.refund_order",
"domain": "orders",
"summary": "Refund an order. Mutating; requires confirmation.",
"mutating": true,
"score": 2.5
}
]
}
--- 3. A normal read call (allowed) ---
OK invoke_tool(invoices.total_outstanding)
{
"tool": "invoices.total_outstanding",
"result": { "outstanding": 1500750 },
"usage": { "requests": 1, "tokens": 9, "maxRequests": 30, "maxTokens": 20000 }
}
--- 4. Argument validation rejects a malformed call ---
DENIED invoke_tool(orders.search_by_status, status:"exploded")
{
"error": "INVALID_ARGUMENTS",
"message": "status: Invalid enum value. Expected 'pending' | 'paid' | 'shipped' | 'cancelled' | 'refunded', received 'exploded'; limit: Number must be less than or equal to 100",
...
}
--- 5. Out-of-scope tool is denied (tenant-style isolation) ---
DENIED invoke_tool(admin.purge_tenant_data)
{
"error": "OUT_OF_SCOPE",
"message": "Tool 'admin.purge_tenant_data' is not in this session's allow-list.",
"details": { "tool": "admin.purge_tenant_data", "tenantId": "acme" }
}
--- 6. Write blocked pending confirmation, then allowed after confirm ---
DENIED invoke_tool(orders.refund_order) — no token
{
"error": "CONFIRMATION_REQUIRED",
"message": "Tool 'orders.refund_order' is mutating and requires confirmation. Call 'confirm_action' with the same tool and arguments to obtain a confirmation_token, then retry.",
"details": { "tool": "orders.refund_order", "mutating": true }
}
OK confirm_action(orders.refund_order)
{
"confirmation_token": "14060221-8c4e-4e1d-a784-b068c624c5a7",
"expires_at": "2026-08-16T06:29:07.880Z",
"single_use": true,
"preview": "orders.refund_order({\"orderId\":\"ORD-1002\",\"amount\":25000}) — mutating action, expires in 120s, single use."
}
OK invoke_tool(orders.refund_order) — with token
{
"tool": "orders.refund_order",
"result": { "orderId": "ORD-1002", "status": "refunded", "refunded": 25000 },
"usage": { "requests": 4, "tokens": 43, "maxRequests": 30, "maxTokens": 20000 }
}
--- 7. Replaying the same confirmation token is rejected (single-use) ---
DENIED invoke_tool(orders.refund_order) — token replayed
{
"error": "CONFIRMATION_REPLAYED",
"message": "Confirmation token has already been used. Tokens are single-use.",
"details": { "tool": "orders.refund_order", "rejection": "replayed" }
}
--- 8. PII redaction on tool output ---
OK invoke_tool(customers.get_by_id)
{
"tool": "customers.get_by_id",
"result": {
"id": "CUST-0002",
"tenantId": "acme",
"name": "Eli Santoso",
"email": "[REDACTED:EMAIL]",
"phone": "[REDACTED:PHONE]",
"tier": "free",
"country": "MY",
"createdAt": "2025-09-18"
},
...
}
--- 9. Rate limit fires on a burst against one tool ---
Rate limit triggered on call #6: {"error":"RATE_LIMITED","message":"Rate limit hit for 'orders.count_by_status'. Retry in 9995ms.","details":{"tool":"orders.count_by_status","retryAfterMs":9995}}
--- 10. Request budget hard cut-off ---
Budget cut-off after 19 further calls -> {"error":"BUDGET_EXCEEDED","message":"Session request budget exhausted (30/30 requests used).","details":{"requestsUsed":30,"maxRequests":30}}
--- 11. Audit log (append-only JSONL) ---
32 entries written to audit/demo-audit.jsonl. First 6:
allowed invoices.total_outstanding ok
denied orders.search_by_status INVALID_ARGUMENTS
denied admin.purge_tenant_data OUT_OF_SCOPE
denied orders.refund_order CONFIRMATION_REQUIRED
allowed orders.refund_order ok
denied orders.refund_order CONFIRMATION_REPLAYED
allowed: 13 denied: 19An audit line in full:
{
"ts": "2026-08-16T06:27:07.874Z",
"sessionId": "635792ad-a119-4067-8dfb-100787b390c8",
"tenantId": "acme",
"tool": "orders.search_by_status",
"args": { "status": "exploded", "limit": 999 },
"decision": "denied",
"durationMs": 1,
"guard": "validation",
"code": "INVALID_ARGUMENTS",
"reason": "status: Invalid enum value. Expected 'pending' | 'paid' | 'shipped' | 'cancelled' | 'refunded', received 'exploded'; limit: Number must be less than or equal to 100"
}Register with Claude Desktop / Claude Code
After npm run build, add to your MCP client config (claude_desktop_config.json, or .mcp.json for Claude Code):
{
"mcpServers": {
"guarded-tools": {
"command": "node",
"args": ["/absolute/path/to/mcp-guarded-tools/dist/server/stdio.js"],
"env": {
"MCP_TENANT_ID": "acme",
"MCP_AUDIT_LOG": "/absolute/path/to/audit/session.jsonl",
"MCP_MAX_REQUESTS": "50",
"MCP_RATE_MAX": "5"
}
}
}
}All env keys are optional. Defaults: tenant acme, 50 requests, 20,000 tokens, 5 calls per 10s window, in-memory audit log.
Design decisions
Why tool-search instead of dumping every schema
Registering 43 tools means every request carries 43 schemas. The measurement above puts that at 2,913 tokens versus 486 — but the stronger argument is selection quality: a model choosing among 4 well-described meta-tools makes a different kind of mistake than one choosing among 43 similarly-named ones. search_tools also gives a natural place to filter by scope, so a session never sees tools it could not call anyway.
The trade-off is latency and the run-time token cost quantified above: two extra round trips before the first real call. The break-even (~10 discovery cycles) is stated rather than hidden.
Search here is BM25-flavoured lexical scoring, not embeddings — deliberately, so the demo runs offline with no model, no index build, and deterministic ranking that tests can assert on. Query-side stopwords are dropped; without that, "money back to a buyer" ranked an unrelated tool first because a and to matched its prose. A production deployment would swap the scorer for a vector or hybrid index behind the same interface; the meta-tool surface would not change.
Why confirmation tokens are single-use, expiring, and argument-bound
A mutating tool call is denied unless it carries a token from confirm_action. That token is:
Single-use — consumed on first successful check. An agent stuck in a retry loop cannot turn one human approval into N writes. The demo shows the replay rejected with
CONFIRMATION_REPLAYED.Time-limited (default 120s) — an approval granted in a different context an hour ago should not authorise a write now.
Bound to a SHA-256 hash of the validated arguments — so approval of "refund 25,000" cannot be redirected to "refund 25,000,000". Argument keys are sorted before hashing, so key order does not matter.
What this does not give you: it is an interlock, not an authorisation system. It assumes the confirming caller is the human (or a trusted supervisor). If the same agent both requests and confirms with no human in the loop, this reduces to a two-step ritual and buys you a preview line in the audit log, nothing more. There is no identity, signature, or approver record on the token.
Why the audit log is append-only JSONL
AuditLog exposes append, all, and bySession — there is no update, delete, or clear method, so no later code path can rewrite history (a test asserts this). Records are one JSON object per line so the file stays greppable, streamable, and survives a partial write: a corrupted tail loses the final record rather than invalidating the whole document the way a truncated JSON array would. Writes are synchronous — an audit record lost on crash is worse than a few microseconds of latency.
This is tamper-evident only in the weak sense that the process never rewrites lines itself. It is not tamper-proof: anyone with write access to the file can edit it. Real immutability needs append-only media (WORM storage, an external log service, or hash-chaining) — none of which is implemented here.
What regex PII redaction does and does not give you
It gives you: removal of well-formed emails, international/grouped phone numbers, 4-group card-shaped numbers, and 16-digit national-ID-shaped numbers, on both inputs and outputs, before values reach the handler, the client, or the audit log. Patterns are configurable and applied narrowest-first (card before phone) because ordering changes the labels.
It does not give you a compliance control. Verified gaps, each reproducible against the shipped patterns:
Input | Result |
| not redacted — 4-6-5 grouping isn't matched |
| not redacted — obfuscated form |
| not redacted — base64-encoded email |
| not redacted — names/addresses have no regex shape |
| redacted, but mislabelled |
Regex redaction reduces casual leakage of structured identifiers. It does not detect PII described in prose, split across fields, encoded, or misspelled, and it will mislabel ambiguous digit runs. Treat it as a blast-radius reducer, not a boundary you can rely on.
What the guardrails do and do not cover
Stated precisely, because "secure" would be the wrong word for all of it:
Scope bounds which tools a session can reach (default-deny allow-list, plus tenant filtering in the data layer as defence in depth). It does not authenticate the caller — there is no identity layer here.
Budgets bound how much a session can consume. Denied calls are charged too, so a retry loop on bad arguments still terminates. It does not distinguish a useful call from a wasteful one.
Rate limits bound how fast, per session and tool. A slow loop stays under them indefinitely — that is what budgets are for.
Validation rejects malformed arguments with field-level messages. It cannot tell a well-formed malicious call from a well-formed legitimate one.
Confirmation puts a human decision point in front of writes, with the caveats above.
Audit makes calls reviewable after the fact. It prevents nothing on its own.
None of these stop a determined attacker who controls the client, and none address prompt injection — a model convinced to call refund_order with plausible arguments will be allowed to, once confirmed. What they do is make the damage from a confused or looping agent bounded and reviewable rather than open-ended.
The demo domain
A fictional multi-tenant B2B commerce back-office, generated from a fixed seed (Mulberry32 PRNG) so every run is reproducible. No external services.
Domain | Tools | Notes |
| 11 | 2 mutating ( |
| 11 | 2 mutating ( |
| 8 | 2 mutating ( |
| 6 | contact fields exercise PII redaction |
| 5 | read-only aggregates |
| 2 | mutating; never granted to demo sessions — demonstrates scope denial |
Total | 43 | 35 read, 8 mutating |
Every read tool filters by tenant in the handler as well as at the scope guard, so a scope misconfiguration still cannot return another tenant's rows.
Tests
62 tests, offline, no credentials:
✓ tests/redaction.test.ts (12) patterns, ordering, nesting, no false positives on IDs/dates/SKUs
✓ tests/registry.test.ts (13) registration, ranking, filters, deterministic dataset
✓ tests/guardrails.test.ts (19) scope, budget, rate limit, validation, confirmation, handler errors
✓ tests/audit.test.ts (7) append-only surface, JSONL on disk, PII never logged raw
✓ tests/e2e.test.ts (11) real client ↔ server over stdio, spawned as a child processThe e2e suite spawns the actual built server and drives it over the real MCP stdio transport — it is not a mock. Confirmation coverage includes expiry, replay, argument-mismatch, tool-mismatch, and unknown tokens.
Project layout
src/
core/ types, config (Zod), session state, budgets, tokens, audit, executor
guardrails/ scope, budget, rate-limit, validation, confirmation, redaction
registry/ tool registry + keyword search
domain/ seeded dataset + 43 demo tools
server/ McpServer wiring, stdio entrypoint
demo/ e2e transcript, token report
tests/ vitest suitesGuardrails implement a single Guardrail interface and are composed as an ordered array in GuardedExecutor, so each is independently testable and the chain is reorderable in one place.
Limitations and what is not built
stdio transport only. The SDK also ships
StreamableHTTPServerTransport; it is not wired up here, and no HTTP/SSE code path is tested.Single session per server process.
buildServer()creates one session; multi-session/multi-tenant routing over one transport is not implemented.In-memory state. Sessions, budgets, and confirmation tokens die with the process. The audit log is the only durable artifact.
No authentication or identity. Scope is configuration, not authorisation.
Search is lexical, not semantic. Stated above as a deliberate offline trade-off.
PII redaction is regex-based, with the verified gaps tabulated above.
The dataset is synthetic; all names, emails, and phone numbers are generated.
License
MIT © Muhammad Ridwan
Available Tools
4 toolsconfirm_actionConfirm a mutating actionA
Issue a single-use, time-limited confirmation token for a mutating tool. The token is bound to the exact arguments supplied here: they must match the later invoke_tool call.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Exact arguments to authorise. | |
| name | Yes | The mutating tool to authorise. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only provide readOnlyHint=false and openWorldHint=false, so the description adds key behavioral details: single-use, time-limited, and bound to exact arguments. This goes beyond the minimal annotation coverage, though it does not detail mismatch behavior or side effects, which prevents a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences front-load the purpose and add the binding constraint without any filler. Every word contributes meaning, making it efficient and well-structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with only two parameters and no output schema, the description covers the core behavior and the relationship to invoke_tool. It omits details like how to pass the token to invoke_tool or expiration specifics, but given the simple scope, it is largely complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents both parameters with descriptions, giving 100% coverage. The description adds value by explaining that the supplied arguments must exactly match a later invoke_tool call, reinforcing the binding semantics beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool issues a single-use, time-limited confirmation token for a mutating tool, using a specific verb and resource. It also distinguishes itself from siblings like invoke_tool by focusing on the confirmation step, and it clarifies that the token is bound to exact arguments.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool should be used before a mutating invoke_tool call, as the token must match that later call. It specifies the applicable case (mutating tools) but does not explicitly list when not to use it or reference alternative tools beyond the implied workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_toolDescribe a toolARead-only
Return the full description and JSON Schema for one tool discovered via search_tools.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Exact tool name, e.g. "orders.refund_order". |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=true, and the description aligns by saying "Return." The description adds context beyond annotations by specifying the exact output content (full description and JSON Schema). No contradictory behavior is disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence, front-loaded with the action and output. No filler or redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter tool with no output schema, the description fully captures what is returned and how to obtain the tool name. The read-only annotation covers safety, and the context with siblings is clear.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides 100% coverage with an example. The description adds a useful hint that the name should come from search_tools, which gives meaning beyond the schema's bare definition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: to return the full description and JSON Schema for one tool. The verb "Return" and resource "description and JSON Schema" are specific, and the mention of "discovered via search_tools" distinguishes it from siblings like search_tools and invoke_tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage after search_tools by stating the tool is "discovered via search_tools." This gives context but does not explicitly exclude other alternatives or state when-not-to-use. It is clear enough given sibling names, but lacks the explicitness of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
invoke_toolInvoke a toolA
Run a domain tool by name. Arguments are validated against the tool schema. Mutating tools additionally require confirmation_token.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | ||
| name | Yes | ||
| confirmation_token | No | Required for mutating tools; obtain from confirm_action. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With only openWorldHint annotation, the description carries the transparency burden. It discloses two behaviors: argument validation against the tool schema and the need for confirmation_token on mutating tools. It does not mention side effects, return values, or error handling, which are relevant for an execution tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no fluff. The first sentence states the core action, the second adds validation and token requirements. Information is front-loaded and every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 3 parameters and no output schema. The description covers the essential execution purpose and the token requirement, but does not explain the overall workflow with sibling tools (e.g., use search_tools/describe_tool to find a tool, confirm_action to get token) or what the tool returns. It is minimally complete but would benefit from workflow context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is low (33%: only confirmation_token documented). The description adds context by stating 'Arguments are validated against the tool schema,' which explains the 'args' parameter's purpose, and 'by name' clarifies 'name.' It also explains the token's role, but does not detail the structure of 'args' or how to obtain valid tool names/confirmations beyond the schema's note to use confirm_action.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly specifies the tool's function: 'Run a domain tool by name.' It uses a specific verb ('run') and resource ('domain tool'), and distinguishes it from sibling tools like search_tools and describe_tool, which are for discovery rather than execution.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage (execute domain tools) but does not explicitly compare with alternatives. It does provide a key conditional: 'Mutating tools additionally require confirmation_token,' which guides when confirmation is needed. However, it lacks explicit 'when not to use' or references to sibling discovery/confirmation workflows.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_toolsSearch toolsARead-only
Find domain tools by keyword. Returns name, domain, one-line summary and whether the tool mutates state. Use this instead of expecting every tool to be listed up front.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | Keywords, e.g. "unpaid invoices" or "refund order". | |
| domain | No | Restrict results to one domain. | |
| mutating | No | Filter to only mutating or only read tools. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false. The description adds useful context about what the tool returns (including 'whether the tool mutates state') and hints that the tool list is not exhaustive. It does not introduce contradictions or disclose additional behaviors beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences: purpose, output, and usage guidance. Every sentence earns its place with no fluff or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description correctly explains return fields. It also provides usage context and alternatives. Minor omission: it does not mention the default limit or pagination, though these are available in the input schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 75%: query, domain, and mutating have descriptions, while limit does not. The description restates the keyword concept and return fields but does not add meaning beyond the schema. It mentions 'domain' as a return field but not as a filter parameter, which could be slightly ambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Find domain tools by keyword.' It also lists the exact return fields (name, domain, one-line summary, state mutation) and distinguishes itself from siblings by saying 'Use this instead of expecting every tool to be listed up front.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context for when to use the tool: 'Use this instead of expecting every tool to be listed up front.' However, it does not explicitly contrast with the sibling tools describe_tool, confirm_action, or invoke_tool, nor does it state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
4 tool updates
v0.1.0- First observed
confirm_action - First observed
describe_tool - First observed
invoke_tool - First observed
search_tools
TDQS
Each tool has a clearly distinct role: discovery (search_tools), inspection (describe_tool), authorization (confirm_action), and execution (invoke_tool). No overlap or ambiguity exists between their purposes.
All tool names follow the same verb_noun pattern with underscores (search_tools, describe_tool, confirm_action, invoke_tool). The naming is perfectly consistent and predictable.
With 4 tools, the set is tightly scoped for a proxy/guardian layer. Each tool earns its place and there is no bloat or missing essential step in the workflow.
The tool set covers the complete lifecycle: discovery, detail inspection, confirmation for mutations, and invocation. There are no obvious gaps; the guard mechanism is fully realized.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
AgentGuard — 20-tool AI safety MCP: policy preflight, risk scoring, audit logging, rate limits.
- gatewayOAuthai.sealgate
MCP gateway with runtime security policy, tool-call-level control, and audit of agent actions.
- WauldoOAuthcom.wauldo
Stateless agentic tools over MCP: concept extraction, long-context, knowledge graph, planning.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA meta-server that aggregates multiple MCP servers into a single interface, reducing token usage by 98%+ through progressive tool discovery and direct code execution that processes data between tools without consuming context window space.1610Apache 2.0
- AlicenseNot gradedqualityFmaintenanceToken-efficient GitLab MCP server that delivers 167 tools through 3 meta-tools with progressive disclosure, field projection, server-side file trimming, and keyset pagination for agent context budgets.2213MIT
- AlicenseNot gradedqualityDmaintenanceToken-optimized MCP server that reduces context window usage by 59.5% by grouping 12 tools into 5 semantic operations, preserving all original functionality for AI assistants.131MIT
- AlicenseNot gradedqualityBmaintenanceAn authenticated MCP gateway that ingests documents and orchestrates hundreds of tools via progressive discovery, keeping context cost constant. It provides per-user RAG over ingested documents and a 116-tool registry that the model navigates through search, describe, and invoke tools.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ridwanspace/mcp-guarded-tools'
If you have feedback or need assistance with the MCP directory API, please join our Discord server