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:
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user