feat(mcp): space-scope the read tool for bound callers

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-04 20:07:22 +10:00
parent 99b1fa445a
commit c955f1eaaf
2 changed files with 48 additions and 1 deletions

View File

@@ -13,11 +13,23 @@ export const readTool = {
},
required: ['kind', 'id']
},
async handler({ kind, id }, _ctx) {
async handler({ kind, id }, ctx = {}) {
const table = TABLE[kind];
if (!table) return { error: `unknown kind "${kind}"` };
const { rows: [row] } = await pool.query(`SELECT * FROM ${table} WHERE id=$1`, [id]);
if (!row) return { error: `${kind} ${id} not found` };
// Space scoping. When a Space is bound (external scoped agents — and Dross,
// which operates within one Space), an entity that carries space_id must
// match it. Kinds without a space_id column (conversations) can't be proven
// in-scope, so they're denied to spaceScoped callers (external agents) only.
// Owner/Dross with no bound Space (ctx.space_id == null) → unrestricted.
if (ctx.space_id != null) {
if (row.space_id !== undefined) {
if (row.space_id !== ctx.space_id) return { error: `${kind} ${id} not found` };
} else if (ctx.spaceScoped) {
return { error: `${kind} ${id} not found` };
}
}
return row;
}
};