mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
The least-privilege half of the criterion could not hold as built. provision_mcp.py creates the endpoint owner's workspace through CoreHandler().create_workspace, which makes that account its ADMIN, and 2.3.3 knows only ADMIN and MEMBER - both write table data. There is no read-only workspace role to demote to, so mutating_tools_enabled: false bounded nothing while create_rows, update_rows and delete_rows sat in the served allowlist. The adapter's contract simply omits them: a client calling one is refused with DENY_UNKNOWN_TOOL. Omission is the mechanism, since passthrough.py refuses to load a contract that lists a mutating tool while mutations are off. The other ten stay recorded under upstream_serves so a version bump enabling one shows up as drift rather than as a silently wider surface. The transport was what reverted this twice. Baserow speaks classic HTTP+SSE, where a GET opens the response stream and the stream names a second URL for requests, while the adapter only ever spoke Streamable HTTP. adapter.upstream_transport selects sse.py and defaults to streamable_http, so no existing provider changes. The endpoint key stops being a client credential. Clients present credentials.mcp_bearer to the sidecar; the key reaches the sidecar as ADAPTER_UPSTREAM_PATH_KEY and is spliced in percent-encoded, so it never enters the rendered contract and a key carrying a separator cannot open a path of its own. Deploy is green: the probe reached list_databases over SSE, the served surface matched the contract, and the sidecar proved it reaches only its provider.
71 lines
1.8 KiB
JSON
71 lines
1.8 KiB
JSON
{
|
|
"get_table_schema": {
|
|
"description": "Get the field schema for one or more tables.",
|
|
"input_schema": {
|
|
"properties": {
|
|
"table_ids": {
|
|
"description": "List of table IDs to get the schema for.",
|
|
"items": {
|
|
"type": "integer"
|
|
},
|
|
"type": "array"
|
|
}
|
|
},
|
|
"required": [
|
|
"table_ids"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"mutating": false
|
|
},
|
|
"list_databases": {
|
|
"description": "List all databases in the workspace. Use this to discover database IDs before listing tables.",
|
|
"input_schema": {
|
|
"properties": {},
|
|
"type": "object"
|
|
},
|
|
"mutating": false
|
|
},
|
|
"list_table_rows": {
|
|
"description": "List rows from a table with optional search and pagination. Returns rows with user-facing field names as keys.",
|
|
"input_schema": {
|
|
"properties": {
|
|
"page": {
|
|
"description": "Page number (1-based).",
|
|
"type": "integer"
|
|
},
|
|
"search": {
|
|
"description": "Optional search term to filter rows.",
|
|
"type": "string"
|
|
},
|
|
"size": {
|
|
"description": "Maximum number of rows to return.",
|
|
"type": "integer"
|
|
},
|
|
"table_id": {
|
|
"description": "The ID of the table to list rows from.",
|
|
"type": "integer"
|
|
}
|
|
},
|
|
"required": [
|
|
"table_id"
|
|
],
|
|
"type": "object"
|
|
},
|
|
"mutating": false
|
|
},
|
|
"list_tables": {
|
|
"description": "List all tables in the workspace, optionally filtered by database.",
|
|
"input_schema": {
|
|
"properties": {
|
|
"database_id": {
|
|
"description": "If provided, only return tables from this database.",
|
|
"type": "integer"
|
|
}
|
|
},
|
|
"type": "object"
|
|
},
|
|
"mutating": false
|
|
}
|
|
}
|