MCP tool to search for files in Microsoft 365 via Microsoft Graph API
Single tool with a descriptive name and basic schema, but parameter descriptions are missing critical detail. Input schema uses Zod and is properly typed. Output schema is unstructured (rich metadata objects without type guarantees). Error handling exists but lacks actionable recovery guidance. No tool annotations (readOnlyHint, idempotentHint). The server does implement proper secret injection (env vars for auth) and follows a stateless request model, but the tool definition falls short of production-grade standards.
Search for files in Microsoft 365 using the Graph API with fallback support
Output schema is unstructured. Handler returns rich nested objects (metadata with file metadata, createdBy, lastModifiedBy, shared scope, downloadUrl) without explicit type documentation. LLM cannot reliably parse the structure to plan downstream operations. No documented field list or types.
'user' parameter description says 'If omitted, defaults to the first user found' but does not explain format or validation. Should specify: 'User UPN (email format, e.g. [email protected]) or objectId (UUID). If omitted, searches the first tenant user.'
Error handling does not guide recovery. Line `searchViaGraphSearchAPI` returns null on !response.ok (no error thrown), then falls back silently. Handler logs to console but does not return error details to the LLM. On failure, the LLM sees only fallback results with no indication of what went wrong. Should return: 'Primary search API failed: {reason}. Falling back to directory listing.'
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 55 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 42 | - | v1 |
No tool annotations. Tool is read-only (READ_ONLY risk declared) but schema does not include readOnlyHint: true. This forces the LLM to infer immutability from the name alone, risking misuse in a multi-tool environment.
No pagination support. Tool does not accept limit or offset parameters, and searches are capped at Graph API defaults. If results are large, no mechanism to fetch next page. Description should state: 'Returns up to 20 results (Graph API default); pagination not yet supported.'
Fallback search is lossy. searchViaFallback lists root/children and filters client-side with .includes(). Does not respect Graph API search syntax (phrase queries, filters). LLM cannot know which search strategy was used, risking wrong assumptions about result relevance.