feat(mcp): model-free document upload via signed URL (#1378)
* feat(mcp): model-free document upload via signed URL (#748) Adds gnubok_create_document_upload + gnubok_complete_document_upload so document bytes reach storage through a short-lived signed PUT URL and never pass through the model context. Fixes silent base64 corruption on real-size PDFs and the context blowup on batch uploads. - pending/ staage keys with TTL cleanup; completion validates magic bytes + SHA-256, moves bytes to the WORM key and adopts the reserved UUID as document id, making retries and concurrent completions idempotent - legacy gnubok_upload_document kept for clients without file access, description now points to the signed-URL pair; shared mime resolution and inbox-item creation extracted - both new tools mapped in TOOL_SCOPE_MAP (transactions:write) and MCP_TOOL_CAPABILITY_MAP (ai) so the paywall and scope gates hold - payload guard ceiling 58.5K to 59K after trimming the create tool's outputSchema to upload_id/upload_url/expires_at Fixes #748 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(mcp): satisfy capability-map lock and phantom-column scanner The exact-entries lock in capability-maps.test.ts now includes the signed-URL pair as dispatch-only AI tools, and the inbox insert uses a literal payload (explicit UUID instead of a conditional spread) so the no-phantom-columns scanner can resolve every column. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
00d4c8a49e
commit
5d7952a01e
@@ -15,19 +15,27 @@ import {
|
||||
/**
|
||||
* MCP tools that invoke a paid capability directly (no stage→commit round-trip),
|
||||
* so they are gated at DISPATCH only and have no commit-time (operation-map)
|
||||
* counterpart. gnubok_upload_document runs Bedrock OCR inline via
|
||||
* extractInvoiceFields: it never stages a pending_operation.
|
||||
* counterpart. The document upload tools run Bedrock OCR inline via
|
||||
* extractInvoiceFields: they never stage a pending_operation.
|
||||
*/
|
||||
const DISPATCH_ONLY_MCP_TOOLS = new Set<string>(['gnubok_upload_document'])
|
||||
const DISPATCH_ONLY_MCP_TOOLS = new Set<string>([
|
||||
'gnubok_upload_document',
|
||||
'gnubok_create_document_upload',
|
||||
'gnubok_complete_document_upload',
|
||||
])
|
||||
|
||||
describe('MCP_TOOL_CAPABILITY_MAP', () => {
|
||||
it('gates exactly the paid MCP tools (3 external-service staging tools + the AI OCR tool)', () => {
|
||||
it('gates exactly the paid MCP tools (3 external-service staging tools + the AI OCR tools)', () => {
|
||||
expect(MCP_TOOL_CAPABILITY_MAP).toEqual({
|
||||
gnubok_send_invoice: CAPABILITY.email_send,
|
||||
gnubok_vat_declaration_submit: CAPABILITY.skatteverket,
|
||||
gnubok_agi_submit: CAPABILITY.skatteverket,
|
||||
// Dispatch-only AI tool: inline Bedrock OCR, no staged operation.
|
||||
// Dispatch-only AI tools: inline Bedrock OCR, no staged operation. The
|
||||
// signed-URL pair is gated at create AND complete so a free-tier key can
|
||||
// neither reserve nor finalize a paid extraction.
|
||||
gnubok_upload_document: CAPABILITY.ai,
|
||||
gnubok_create_document_upload: CAPABILITY.ai,
|
||||
gnubok_complete_document_upload: CAPABILITY.ai,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -65,17 +65,19 @@ export const PAID_CAPABILITIES: readonly CapabilityKey[] = [
|
||||
* read/local SKV tools (generate_agi, vat_declaration_validate/status, agi_status)
|
||||
* stay free: the §4 carve-out forbids blocking a statutory filing obligation.
|
||||
*
|
||||
* gnubok_upload_document invokes AI (Bedrock document OCR via
|
||||
* extractInvoiceFields), so it is gated on CAPABILITY.ai: the same paywall the
|
||||
* HTTP inbox upload/attach/retry paths enforce. Without this entry a free-tier
|
||||
* API key (incl. the claude.ai connector's minted gnubok_sk_ key) could trigger
|
||||
* paid AI extraction. bank_sync has no MCP tool (bank sync is cron/HTTP only).
|
||||
* The document upload tools invoke AI (Bedrock document OCR via
|
||||
* extractInvoiceFields), so they are gated on CAPABILITY.ai: the same paywall
|
||||
* the HTTP inbox upload/attach/retry paths enforce. Without these entries a
|
||||
* free-tier API key could trigger paid AI extraction. bank_sync has no MCP
|
||||
* tool (bank sync is cron/HTTP only).
|
||||
*/
|
||||
export const MCP_TOOL_CAPABILITY_MAP: Readonly<Partial<Record<string, CapabilityKey>>> = {
|
||||
gnubok_send_invoice: CAPABILITY.email_send,
|
||||
gnubok_vat_declaration_submit: CAPABILITY.skatteverket,
|
||||
gnubok_agi_submit: CAPABILITY.skatteverket,
|
||||
// AI document OCR (Bedrock): the inbox's paid extraction, reachable via MCP.
|
||||
gnubok_create_document_upload: CAPABILITY.ai,
|
||||
gnubok_complete_document_upload: CAPABILITY.ai,
|
||||
gnubok_upload_document: CAPABILITY.ai,
|
||||
} as const
|
||||
|
||||
|
||||
Reference in New Issue
Block a user