feat: validate receive files list args

This commit is contained in:
zhaoyilun
2026-07-10 01:43:11 +08:00
parent b1891dd6cd
commit fce1232f21
7 changed files with 155 additions and 16 deletions

View File

@@ -28,6 +28,7 @@ Remote base before local roadmap commits: `b2d839e Merge branch 'codex/n15-repor
- `isphere_receive_messages` now returns the contract-facing `ok`, `conversation`, `messages`, `next_cursor`, and `audit` envelope while retaining legacy parser-native message fields for compatibility. - `isphere_receive_messages` now returns the contract-facing `ok`, `conversation`, `messages`, `next_cursor`, and `audit` envelope while retaining legacy parser-native message fields for compatibility.
- `isphere_receive_messages` accepts the read-contract argument set for the current local-readonly path: `conversation_id`, `query`, `since`, `limit`, `include_attachment_metadata`, `source_preference`, `preview`, and empty `cursor`. - `isphere_receive_messages` accepts the read-contract argument set for the current local-readonly path: `conversation_id`, `query`, `since`, `limit`, `include_attachment_metadata`, `source_preference`, `preview`, and empty `cursor`.
- `isphere_search_contacts` and `isphere_search_groups` accept and validate the safe search-contract args for the current local-readonly path. - `isphere_search_contacts` and `isphere_search_groups` accept and validate the safe search-contract args for the current local-readonly path.
- `isphere_receive_files` list mode accepts and validates the safe file-list contract args for the current local-readonly path while keeping download blocked.
- N12-pre and N12R documents define safe offline evidence intake and internal-sandbox live UIA capture procedures. - N12-pre and N12R documents define safe offline evidence intake and internal-sandbox live UIA capture procedures.
- N13/N14/N15 produced selector catalog/report validation infrastructure, but those are supporting validation assets only. - N13/N14/N15 produced selector catalog/report validation infrastructure, but those are supporting validation assets only.
@@ -54,7 +55,7 @@ N13/N14/N15 are pre-business validation results. They can help identify UI eleme
## Current loop ## Current loop
Current loop: `Stage C / Loop C22 - file receive remaining contract argument validation`. Current loop: `Stage C / Loop C23 - contact/group display-name source precheck`.
Plan file: `docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md`. Plan file: `docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md`.
@@ -88,10 +89,11 @@ Current loop output so far:
19. C19: `isphere_receive_messages` gained RFC3339 `since` filtering against PacketReader epoch-millisecond timestamps plus invalid-since error handling. 19. C19: `isphere_receive_messages` gained RFC3339 `since` filtering against PacketReader epoch-millisecond timestamps plus invalid-since error handling.
20. C20: `isphere_receive_messages` now accepts and validates the remaining safe read-contract args, rejects unsupported source/cursor cases, and can suppress inline attachment metadata. 20. C20: `isphere_receive_messages` now accepts and validates the remaining safe read-contract args, rejects unsupported source/cursor cases, and can suppress inline attachment metadata.
21. C21: `isphere_search_contacts` and `isphere_search_groups` now accept and validate safe search-contract args, while rejecting unsupported source/cursor cases. 21. C21: `isphere_search_contacts` and `isphere_search_groups` now accept and validate safe search-contract args, while rejecting unsupported source/cursor cases.
22. C22: `isphere_receive_files` list mode now accepts and validates safe contract args, rejects unsupported source/cursor/output/download cases, and keeps download blocked.
## Next business mainline ## Next business mainline
1. Run Stage C Loop C22: apply the same safe-argument validation pattern to `isphere_receive_files` list mode while keeping download blocked. 1. Run Stage C Loop C23: inspect whether current evidence can enrich contact/group display names beyond JID-only identities.
2. Leave `isphere_send_message` blocked until bridge/API, UIA action-chain, or protocol connector evidence is validated. 2. Leave `isphere_send_message` blocked until bridge/API, UIA action-chain, or protocol connector evidence is validated.
3. Leave actual file download/cache mapping as a later connector node until a message-to-cache hash or client download source is validated. 3. Leave actual file download/cache mapping as a later connector node until a message-to-cache hash or client download source is validated.
4. Keep UIA selector/report work as fallback support, not as the primary roadmap. 4. Keep UIA selector/report work as fallback support, not as the primary roadmap.

View File

@@ -187,7 +187,7 @@ Allowed parameters:
- `isphere_receive_messages`: `conversation_id`, `query`, `since`, `include_attachment_metadata`, `source_preference`, `preview`, `cursor`, `limit` only. `since` must be RFC3339/RFC3339Nano when supplied; `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` must be empty until pagination is implemented. - `isphere_receive_messages`: `conversation_id`, `query`, `since`, `include_attachment_metadata`, `source_preference`, `preview`, `cursor`, `limit` only. `since` must be RFC3339/RFC3339Nano when supplied; `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` must be empty until pagination is implemented.
- `isphere_search_contacts`: `query`, `cursor`, `source_preference`, `include_inactive`, `limit` only. `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` must be empty until pagination is implemented. - `isphere_search_contacts`: `query`, `cursor`, `source_preference`, `include_inactive`, `limit` only. `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` must be empty until pagination is implemented.
- `isphere_search_groups`: `query`, `cursor`, `source_preference`, `include_archived`, `limit` only. `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` must be empty until pagination is implemented. - `isphere_search_groups`: `query`, `cursor`, `source_preference`, `include_archived`, `limit` only. `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` must be empty until pagination is implemented.
- `isphere_receive_files`: `conversation_id`, `message_id`, `file_id`, `name_contains`, `mode`, `limit` only. C14 supports `mode` empty or `list`; download is deferred. - `isphere_receive_files`: `conversation_id`, `message_id`, `file_id`, `name_contains`, `mode`, `output_dir`, `cursor`, `source_preference`, `preview`, `limit` only. C22 supports `mode` empty or `list`; `source_preference` currently supports empty/`auto`/`local_readonly`; `cursor` and `output_dir` must be empty in current list-only mode; download is deferred.
## 9. Current tool surface and next-stage route ## 9. Current tool surface and next-stage route
@@ -202,7 +202,7 @@ The current runbook verifies eight tools:
- `isphere_search_groups` - `isphere_search_groups`
- `isphere_receive_files` - `isphere_receive_files`
The current log-backed business read tools have parser/source/tool registration and an empty default server source. Raw N12-pre evidence remains under ignored `runs/` paths and is not committed. The current command entry point can read an operator-local encrypted PacketReader log-line file via `ISPHERE_PACKET_LOG_FILE` or a directory of `.log`/`.txt` files via `ISPHERE_PACKET_LOG_DIR`; this is still not a production ingestion claim because raw evidence remains local and uncommitted. `isphere_receive_messages` now exposes contract-facing fields for digital-employee consumption while preserving older aliases for compatibility, supports `conversation_id`, keyword `query`, and RFC3339 `since` filtering, and validates the remaining safe contract args without pretending unsupported connectors/pagination exist. Contact and group search also accept the safe local-readonly contract args. `isphere_receive_files` currently supports list mode only; download/cache mapping is deferred. The current log-backed business read tools have parser/source/tool registration and an empty default server source. Raw N12-pre evidence remains under ignored `runs/` paths and is not committed. The current command entry point can read an operator-local encrypted PacketReader log-line file via `ISPHERE_PACKET_LOG_FILE` or a directory of `.log`/`.txt` files via `ISPHERE_PACKET_LOG_DIR`; this is still not a production ingestion claim because raw evidence remains local and uncommitted. `isphere_receive_messages` now exposes contract-facing fields for digital-employee consumption while preserving older aliases for compatibility, supports `conversation_id`, keyword `query`, and RFC3339 `since` filtering, and validates the remaining safe contract args without pretending unsupported connectors/pagination exist. Contact and group search also accept the safe local-readonly contract args. `isphere_receive_files` supports list mode with safe contract arg validation; download/cache mapping remains deferred.
Core business tools for contacts, groups, messages, and files are defined in `docs/mcp-core-tools-contract.md`. Send capabilities enter the roadmap through that contract after source discovery, connector selection, preview metadata, execution metadata, and audit records are in place. Core business tools for contacts, groups, messages, and files are defined in `docs/mcp-core-tools-contract.md`. Send capabilities enter the roadmap through that contract after source discovery, connector selection, preview metadata, execution metadata, and audit records are in place.

View File

@@ -20,7 +20,7 @@ Schema notes: `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md`
| `isphere_receive_messages` | decrypted `PacketReader.ProcessPacket` XMPP `<message>` stanzas via `internal/isphere.EncryptedPacketLogSource`; configured by `ISPHERE_PACKET_LOG_FILE` or `ISPHERE_PACKET_LOG_DIR` | decrypted `Smark.SendReceive` transport stanzas; decrypted `SaveToDB` `Chat_OnMessageArrived` traces; wrapped `MsgLib.db` after DB wrapper is solved | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#field-mapping-evidence` | C20 supports and validates the current safe receive-message contract args; default source remains empty when unset | apply safe contract args to contact/group search | | `isphere_receive_messages` | decrypted `PacketReader.ProcessPacket` XMPP `<message>` stanzas via `internal/isphere.EncryptedPacketLogSource`; configured by `ISPHERE_PACKET_LOG_FILE` or `ISPHERE_PACKET_LOG_DIR` | decrypted `Smark.SendReceive` transport stanzas; decrypted `SaveToDB` `Chat_OnMessageArrived` traces; wrapped `MsgLib.db` after DB wrapper is solved | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#field-mapping-evidence` | C20 supports and validates the current safe receive-message contract args; default source remains empty when unset | apply safe contract args to contact/group search |
| `isphere_search_contacts` | bare sender/receiver JIDs extracted from normalized log-backed messages loaded from configured PacketReader file or directory source | decrypted roster/contact stanzas from `Smark.SendReceive`; `MsgLib.db` contact/conversation tables after DB wrapper is solved; UIA helper source if display names are missing | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#log-decryption-result` | C21 verifies safe search-contract args plus default/configured contact smokes; JID-only display/account fallback | enrich display names from roster/db/uia evidence | | `isphere_search_contacts` | bare sender/receiver JIDs extracted from normalized log-backed messages loaded from configured PacketReader file or directory source | decrypted roster/contact stanzas from `Smark.SendReceive`; `MsgLib.db` contact/conversation tables after DB wrapper is solved; UIA helper source if display names are missing | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#log-decryption-result` | C21 verifies safe search-contract args plus default/configured contact smokes; JID-only display/account fallback | enrich display names from roster/db/uia evidence |
| `isphere_search_groups` | decrypted `PacketReader.ProcessPacket` `type="groupchat"` stanzas and conference/MUC JIDs loaded from configured PacketReader file or directory source | `MsgLib.db` group/conversation tables after DB wrapper is solved; UIA helper source if group display names are missing | C9 ignored-log scan: PacketReader 86 groupchat/86 conference hits; Smark 24 groupchat/658 conference/631 muc hits | C21 verifies safe search-contract args plus default/configured group smokes; JID-only display fallback | enrich group display/member data from roster/db/uia evidence | | `isphere_search_groups` | decrypted `PacketReader.ProcessPacket` `type="groupchat"` stanzas and conference/MUC JIDs loaded from configured PacketReader file or directory source | `MsgLib.db` group/conversation tables after DB wrapper is solved; UIA helper source if group display names are missing | C9 ignored-log scan: PacketReader 86 groupchat/86 conference hits; Smark 24 groupchat/658 conference/631 muc hits | C21 verifies safe search-contract args plus default/configured group smokes; JID-only display fallback | enrich group display/member data from roster/db/uia evidence |
| `isphere_receive_files` | decrypted `PacketReader.ProcessPacket` file-transfer message stanzas via `internal/isphere.ListFilesFromMessages` and `isphere_receive_files` list mode; configured PacketReader file or directory source; `zyl\importal\<hash>` cache entries remain unlinked | decrypted `Smark.SendReceive` and `SaveToDB` traces for file-reference reconciliation; future UIA/client connector for download | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#c12-file-transfer-evidence-precheck` | C16 verification covers default, configured-file, and configured-directory file-list smoke; no log-to-cache/download mapping yet | defer download mapping; align read-tool contract output | | `isphere_receive_files` | decrypted `PacketReader.ProcessPacket` file-transfer message stanzas via `internal/isphere.ListFilesFromMessages` and `isphere_receive_files` list mode; configured PacketReader file or directory source; `zyl\importal\<hash>` cache entries remain unlinked | decrypted `Smark.SendReceive` and `SaveToDB` traces for file-reference reconciliation; future UIA/client connector for download | `docs/source-discovery/2026-07-09-n12-pre-zyl-schema-notes.md#c12-file-transfer-evidence-precheck` | C22 verifies safe file-list contract args; no log-to-cache/download mapping yet | defer download mapping; precheck contact/group display-name evidence |
| `isphere_send_message` | none validated yet | existing bridge/API/local service preferred; UIA write connector only after internal-sandbox action evidence; network/protocol connector only after protocol discovery | `docs/source-discovery/2026-07-10-send-message-source-precheck.md` | C15 blocked: contract exists, but no committed bridge/API/local service, helper write op, UIA action chain, or protocol connector is validated | do not implement write behavior; harden existing read source configuration | | `isphere_send_message` | none validated yet | existing bridge/API/local service preferred; UIA write connector only after internal-sandbox action evidence; network/protocol connector only after protocol discovery | `docs/source-discovery/2026-07-10-send-message-source-precheck.md` | C15 blocked: contract exists, but no committed bridge/API/local service, helper write op, UIA action chain, or protocol connector is validated | do not implement write behavior; harden existing read source configuration |
| `isphere_send_file` | none validated yet | depends on send-message connector plus outbound file/upload evidence | `docs/source-discovery/2026-07-10-send-message-source-precheck.md` | blocked behind `isphere_send_message` connector selection and file upload/source policy | defer until send-message connector is validated | | `isphere_send_file` | none validated yet | depends on send-message connector plus outbound file/upload evidence | `docs/source-discovery/2026-07-10-send-message-source-precheck.md` | blocked behind `isphere_send_message` connector selection and file upload/source policy | defer until send-message connector is validated |

View File

@@ -1816,19 +1816,19 @@ Implemented and verified in this loop:
- Accept empty `output_dir` in list mode; reject non-empty `output_dir` unless/until download mode is implemented to avoid implying files were written. - Accept empty `output_dir` in list mode; reject non-empty `output_dir` unless/until download mode is implemented to avoid implying files were written.
- Keep mode empty/`list` only; continue rejecting `download`. - Keep mode empty/`list` only; continue rejecting `download`.
- [ ] **Step 1: Write failing file MCP arg tests** - [x] **Step 1: Write failing file MCP arg tests**
Extend `internal/tools/isphere_files_test.go` with accepted safe args and rejected unsupported source/cursor/output/download cases. Extend `internal/tools/isphere_files_test.go` with accepted safe args and rejected unsupported source/cursor/output/download cases.
- [ ] **Step 2: Implement file arg validation** - [x] **Step 2: Implement file arg validation**
Reuse local-readonly source/cursor validation and add output/download list-mode checks. Reuse local-readonly source/cursor validation and add output/download list-mode checks.
- [ ] **Step 3: Update verification and docs** - [x] **Step 3: Update verification and docs**
Update `scripts/verify-go-mcp.ps1`, runbook, status card, matrix, and this plan. Update `scripts/verify-go-mcp.ps1`, runbook, status card, matrix, and this plan.
- [ ] **Step 4: Run full verification and commit** - [x] **Step 4: Run full verification and commit**
Run: Run:
@@ -1843,6 +1843,58 @@ Expected: all exit 0. Remove any generated root `isphere-mcp.exe` after build. C
--- ---
## Loop C22 Result
Implemented and verified in this loop:
- `isphere_receive_files` list mode now accepts safe contract args `cursor`, `source_preference`, `preview`, and `output_dir`.
- It accepts empty/`auto`/`local_readonly` source preference and rejects unsupported connector selections until those sources exist.
- It accepts empty cursor and rejects non-empty cursor until pagination exists.
- It accepts empty `output_dir` in list mode and rejects non-empty `output_dir` to avoid implying a download/write happened.
- It continues to reject `download` mode until message-to-cache/download mapping is validated.
- `scripts/verify-go-mcp.ps1` now exercises file-list safe args in default and configured source smokes.
---
## Loop C23: Contact/group display-name source precheck
**Goal:** Decide whether the current N12-pre evidence contains a safe source for human-readable contact/group display names, because current contact/group tools expose JID-derived identities only.
**Planned files:**
- Modify: `docs/source-discovery/capability-source-matrix.md`
- Modify: `docs/current-status-card.md`
- Modify: `docs/superpowers/plans/2026-07-09-stage-c-log-backed-receive-messages-loop.md`
- Optionally create a focused source-discovery note under `docs/source-discovery/` if evidence is non-trivial.
- Optionally create ignored metadata under `runs/`; do not commit raw logs or user content.
**Planned behavior:**
- Inspect committed schema notes and ignored metadata/log summaries for roster/contact display names, group display names, aliases, departments, or conversation-title fields.
- If evidence is enough, write C24 as a source implementation loop for display-name enrichment.
- If evidence is not enough, keep JID-only behavior explicit and choose the next business-read hardening node.
- Do not add write/send/file-download behavior.
- [ ] **Step 1: Inspect current evidence**
Use committed source-discovery docs first; only inspect ignored local summaries/counts if needed. Do not commit raw evidence.
- [ ] **Step 2: Update matrix/status and next loop**
Record whether display-name enrichment can proceed and rewrite C24 accordingly.
- [ ] **Step 3: Verify and commit docs**
Run:
```powershell
git diff --check
go test ./...
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/verify-go-mcp.ps1
```
Expected: all exit 0. Commit exact C23 paths only and rewrite the next loop from actual results.
---
## Self-Review ## Self-Review
- Spec coverage: the plan follows the user's cyclic requirement: plan first, implement one loop at a time, update the next loop after each result, and ask only on blockers. - Spec coverage: the plan follows the user's cyclic requirement: plan first, implement one loop at a time, update the next loop after each result, and ask only on blockers.

View File

@@ -14,12 +14,16 @@ import (
const ToolNameReceiveFiles = "isphere_receive_files" const ToolNameReceiveFiles = "isphere_receive_files"
type ReceiveFilesArgs struct { type ReceiveFilesArgs struct {
ConversationID string `json:"conversation_id,omitempty" jsonschema:"conversation id filter for file listing"` ConversationID string `json:"conversation_id,omitempty" jsonschema:"conversation id filter for file listing"`
MessageID string `json:"message_id,omitempty" jsonschema:"message id filter for file listing"` MessageID string `json:"message_id,omitempty" jsonschema:"message id filter for file listing"`
FileID string `json:"file_id,omitempty" jsonschema:"file id filter for file listing"` FileID string `json:"file_id,omitempty" jsonschema:"file id filter for file listing"`
NameContains string `json:"name_contains,omitempty" jsonschema:"case-insensitive filename substring filter"` NameContains string `json:"name_contains,omitempty" jsonschema:"case-insensitive filename substring filter"`
Mode string `json:"mode,omitempty" jsonschema:"file receive mode; C14 supports list only"` Mode string `json:"mode,omitempty" jsonschema:"file receive mode; C14 supports list only"`
Limit int `json:"limit,omitempty" jsonschema:"maximum number of files to return; zero or negative returns all matches"` OutputDir string `json:"output_dir,omitempty" jsonschema:"download output directory; not accepted in current list-only mode"`
Cursor string `json:"cursor,omitempty" jsonschema:"pagination cursor; currently only empty cursor is supported"`
SourcePreference string `json:"source_preference,omitempty" jsonschema:"source selector; currently supports auto or local_readonly only"`
Preview bool `json:"preview,omitempty" jsonschema:"accepted for contract compatibility in list mode"`
Limit int `json:"limit,omitempty" jsonschema:"maximum number of files to return; zero or negative returns all matches"`
} }
func RegisterISphereFileTools(server *mcp.Server, source ReceiveMessagesSource) { func RegisterISphereFileTools(server *mcp.Server, source ReceiveMessagesSource) {
@@ -39,6 +43,12 @@ func RegisterISphereFileTools(server *mcp.Server, source ReceiveMessagesSource)
if mode != "list" { if mode != "list" {
return nil, nil, fmt.Errorf("isphere_receive_files mode %q is not available; only list is supported", input.Mode) return nil, nil, fmt.Errorf("isphere_receive_files mode %q is not available; only list is supported", input.Mode)
} }
if err := validateLocalReadonlySourceAndCursor(ToolNameReceiveFiles, input.SourcePreference, input.Cursor); err != nil {
return nil, nil, err
}
if strings.TrimSpace(input.OutputDir) != "" {
return nil, nil, fmt.Errorf("isphere_receive_files output_dir is only available for download mode, which is not implemented yet")
}
messages, err := source.ReceiveMessages(ctx, isphere.ReceiveMessagesQuery{Limit: 0}) messages, err := source.ReceiveMessages(ctx, isphere.ReceiveMessagesQuery{Limit: 0})
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -88,3 +88,70 @@ func TestISphereReceiveFilesToolReturnsLogBackedFiles(t *testing.T) {
t.Fatalf("source queries = %+v, want one unbounded receive query", fake.queries) t.Fatalf("source queries = %+v, want one unbounded receive query", fake.queries)
} }
} }
func TestISphereReceiveFilesToolValidatesContractArgs(t *testing.T) {
fake := &fakeReceiveMessagesSource{
result: isphere.ReceiveMessagesResult{Messages: []isphere.Message{{
ID: "msg-file-1",
ConversationID: "alice@imopenfire1-lanzhou|bob@imopenfire1-lanzhou",
Text: "redacted-report.docx",
Timestamp: "1783423807000",
Subject: "FILE_TRANSFER_CANCEL",
}}},
}
session, cleanup := connectToolsTestSession(t, func(server *mcp.Server) {
RegisterISphereFileTools(server, fake)
})
defer cleanup()
callResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{
Name: ToolNameReceiveFiles,
Arguments: map[string]any{
"mode": "list",
"name_contains": "report",
"source_preference": "local_readonly",
"preview": true,
"cursor": "",
"output_dir": "",
"limit": 5,
},
})
if err != nil {
t.Fatalf("call with safe contract args: %v", err)
}
if callResult.IsError {
t.Fatalf("call result is error: %+v", callResult)
}
bridgeResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{
Name: ToolNameReceiveFiles,
Arguments: map[string]any{"mode": "list", "source_preference": "bridge"},
})
if err == nil && !bridgeResult.IsError {
t.Fatalf("bridge source_preference was accepted before connector exists")
}
cursorResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{
Name: ToolNameReceiveFiles,
Arguments: map[string]any{"mode": "list", "cursor": "next-page"},
})
if err == nil && !cursorResult.IsError {
t.Fatalf("non-empty cursor was accepted before pagination exists")
}
outputResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{
Name: ToolNameReceiveFiles,
Arguments: map[string]any{"mode": "list", "output_dir": "C:\\tmp\\isphere"},
})
if err == nil && !outputResult.IsError {
t.Fatalf("output_dir was accepted in list mode before download exists")
}
downloadResult, err := session.CallTool(context.Background(), &mcp.CallToolParams{
Name: ToolNameReceiveFiles,
Arguments: map[string]any{"mode": "download", "file_id": "msg-file-1:redacted-report.docx"},
})
if err == nil && !downloadResult.IsError {
t.Fatalf("download mode was accepted before cache mapping exists")
}
}

View File

@@ -567,7 +567,15 @@ func verifySearchGroups(ctx context.Context, session *mcp.ClientSession, query s
func verifyReceiveFiles(ctx context.Context, session *mcp.ClientSession, nameContains string, step string, wantCount int, wantFileID string, wantFileName string) int { func verifyReceiveFiles(ctx context.Context, session *mcp.ClientSession, nameContains string, step string, wantCount int, wantFileID string, wantFileName string) int {
callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_receive_files", Arguments: map[string]any{"mode": "list", "name_contains": nameContains, "limit": 5}}) callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_receive_files", Arguments: map[string]any{
"mode": "list",
"name_contains": nameContains,
"source_preference": "local_readonly",
"preview": true,
"cursor": "",
"output_dir": "",
"limit": 5,
}})
if err != nil { if err != nil {
fail(step, err) fail(step, err)
} }