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:
@@ -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` |
|
||||
|
||||
@@ -24,6 +24,7 @@ var curatedOperationIDs = []string{
|
||||
"MemoService_ListMemoRelations",
|
||||
"MemoService_SetMemoRelations",
|
||||
"AttachmentService_ListAttachments",
|
||||
"AttachmentService_CreateAttachment",
|
||||
"AttachmentService_GetAttachment",
|
||||
"AttachmentService_DeleteAttachment",
|
||||
"ShortcutService_ListShortcuts",
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user