Bamboo MCP Gateway has 5 tools with explicit schemas and descriptions visible in src/mcp-server.ts. However, the server suffers from critical gaps in parameter descriptions, output schema documentation, error handling guidance, and security considerations. The tools follow a namespace.verb naming pattern (ads.get_*, pg.query, pg.get_tables) which is reasonable, but parameter descriptions are minimal or absent. Most critically: pg.query accepts arbitrary SQL without input validation documentation, poses SQL injection risk, and lacks any error recovery guidance. Output schemas are entirely undocumented, the code registers tools but never documents what the tools return, forcing LLMs to guess structure. No tool annotations (readOnlyHint/destructiveHint) are present despite clear distinctions between read and write operations. Parameters like 'params' in pg.query lack type constraints (array items should not all be 'string'). No confirmation patterns for destructive operations (pg.query with DELETE/DROP). The implementation shows the basic MCP scaffolding (tools/list, tools/call, resources/list, resources/read) but definition quality is below median community standard.
CRITICAL: pg.query accepts arbitrary SQL without validation, sanitization, or SQL injection safeguards documented. No explanation of parameterized query support or how to prevent injection.
Output schemas completely undocumented. Tools are registered but their return types, field names, and data structures are never documented. LLMs cannot plan downstream calls or extract data reliably.
Tool descriptions are far too brief (20-35 chars vs. rubric baseline of 194 chars avg). Descriptions like 'Get Meta Ads campaigns' lack WHEN to use, prerequisites, or dependencies on other tools.
Recommendations
Expand all tool descriptions to 100-250 characters. For ads.get_campaigns: 'Retrieve Meta Ads campaigns from a specific ad account. Required for discovering campaign IDs needed by get_adsets. Returns campaign name, ID, status, and spend totals. Requires account_id (e.g., "act_123456"). Limit defaults to 25, use offset for pagination.'
Add strict input validation and error messages to pg.query. Return 'Invalid SQL: detected DROP/TRUNCATE. Use read-only queries or contact admin.' for destructive ops. Return 'SQL Error: <detail>. Check syntax and parameter types.' for parse failures.
Implement parameterized query framework. Change pg.query to enforce prepared statements with separate query + typed params. Update schema: params: {type: 'object', properties: {param_name: {type: 'string|number|boolean'}}} to support multiple types.
Add tool annotations to input schemas. For ads.get_campaigns and all reads: append '"readOnlyHint": true'. For pg.query: append '"destructiveHint": true, "idempotentHint": false'. (Note: annotations property structure depends on MCP spec version, verify current format.)
Add pagination support. For ads tools: add offset/cursor parameter and return total_count and next_cursor. Document: 'If results are truncated, use offset + limit or next_cursor for subsequent calls.'
Implement dry-run confirmation for pg.query. Add optional dry_run boolean param. If true and query is destructive, return 'Would delete X rows. Set dry_run: false to execute.' This prevents accidental data loss.
Spec posture evidence
Inferred effective spec: <=2025-11-25.
Relies on Logging (deprecated) - log to stderr or use OpenTelemetry
Parameter descriptions are minimal or missing. 'params' in pg.query is described only as 'Query parameters' but does not specify format, how to construct them, or safety requirements.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) present despite clear semantic differences. All ads.get_* tools are read-only; pg.query is destructive; neither distinction is signaled.
No error recovery guidance. handleToolCall/handleRequest catch errors and call sendError but provide no actionable guidance for the LLM (no 'try search_* first', no 'retry-safe' classification).
Pagination not implemented for list tools (ads.get_campaigns/adsets/ads). If account has 1000 campaigns, tool will fail or return unpredictable subset. No limit parameter documented in schema.
Meta Ads credentials (API tokens) must be injected server-side but no documentation on how. If credentials appear as tool parameters, they will be logged and leaked.
pg.query 'params' field is typed as array of strings only. Real SQL often requires numbers, booleans, dates, UUIDs. Schema is too restrictive.
pg.query
Add permission/scope declarations. Each tool should state required permissions (e.g., ads.get_campaigns requires 'read:ads'). Gate tool access with permission checks before execution.
Document credential injection. Add section to README: 'Meta Ads tokens are injected via environment variables (META_API_TOKEN). They are never exposed as tool parameters. PostgreSQL connection uses PG_CONNECTION_STRING or individual PG_* vars.'
Add recovery guidance to error responses. E.g., if ads.get_campaigns fails with 'Account not found', return error: {code: 404, message: 'Ad account not found', recovery: 'Verify account_id format (e.g., act_123456). Call list_accounts to find valid IDs.'}
Reduce reliance on server-side implementation details in descriptions. Make descriptions teach the LLM: 'Get campaigns to discover campaign IDs for use in get_adsets and get_ads. Chains: get_campaigns → get_adsets → get_ads.'
Add examples to resource URIs (bamboo://company/context, bamboo://meta-ads/schema, bamboo://database/schema) showing what data structure each resource exposes. LLMs need to know what these resolve to.