feat(mcp): expose create_attachment tool

Add AttachmentService_CreateAttachment to the curated MCP allowlist so
agents can upload files (inline base64 content) alongside memos, closing
the gap where the MCP server could list/get/delete attachments but not
create them.

Closes #6057
This commit is contained in:
boojack
2026-07-01 22:15:58 +08:00
parent 0820fc2d6c
commit 0e1d821fb8
3 changed files with 39 additions and 1 deletions
+1
View File
@@ -154,6 +154,7 @@ resolve its own user — the single allowed auth/identity operation):
| `MemoService_ListMemoRelations` | `memo_list_memo_relations` |
| `MemoService_SetMemoRelations` | `memo_set_memo_relations` |
| `AttachmentService_ListAttachments` | `attachment_list_attachments` |
| `AttachmentService_CreateAttachment` | `attachment_create_attachment` |
| `AttachmentService_GetAttachment` | `attachment_get_attachment` |
| `AttachmentService_DeleteAttachment` | `attachment_delete_attachment` |
| `ShortcutService_ListShortcuts` | `shortcut_list_shortcuts` |
+1
View File
@@ -24,6 +24,7 @@ var curatedOperationIDs = []string{
"MemoService_ListMemoRelations",
"MemoService_SetMemoRelations",
"AttachmentService_ListAttachments",
"AttachmentService_CreateAttachment",
"AttachmentService_GetAttachment",
"AttachmentService_DeleteAttachment",
"ShortcutService_ListShortcuts",
+37 -1
View File
@@ -9,7 +9,7 @@ import (
)
func TestCuratedOperationIDsStayMemoFocused(t *testing.T) {
require.Len(t, curatedOperationIDs, 19)
require.Len(t, curatedOperationIDs, 20)
for _, operationID := range curatedOperationIDs {
require.NotContains(t, operationID, "Admin")
@@ -104,6 +104,42 @@ func TestBuildToolFromOperationIncludesRequestBodySchema(t *testing.T) {
require.NoError(t, err)
}
func TestBuildToolFromOperationExposesCreateAttachment(t *testing.T) {
spec, err := loadOpenAPISpec("../../../proto/gen/openapi.yaml")
require.NoError(t, err)
registry, err := buildOperationRegistry(spec)
require.NoError(t, err)
tool, operation := buildToolFromOperation(registry["AttachmentService_CreateAttachment"])
require.Equal(t, "attachment_create_attachment", tool.Name)
require.Equal(t, "POST", operation.Method)
require.False(t, tool.Annotations.ReadOnlyHint)
require.False(t, *tool.Annotations.DestructiveHint)
require.False(t, tool.Annotations.IdempotentHint)
input, ok := tool.InputSchema.(jsonSchema)
require.True(t, ok)
require.Contains(t, input["required"], "body")
properties, ok := input["properties"].(map[string]any)
require.True(t, ok)
// attachmentId is an optional query parameter; the file itself is the body.
require.Contains(t, properties, "attachmentId")
require.Contains(t, properties, "body")
body, ok := properties["body"].(jsonSchema)
require.True(t, ok)
require.Contains(t, body["properties"], "filename")
require.Contains(t, body["properties"], "content")
err = validateToolArguments(input, map[string]any{
"body": map[string]any{
"filename": "screenshot.png",
"type": "image/png",
"content": "aGVsbG8=",
},
})
require.NoError(t, err)
}
func TestBuildToolFromOperationExposesCurrentUser(t *testing.T) {
spec, err := loadOpenAPISpec("../../../proto/gen/openapi.yaml")
require.NoError(t, err)