Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Schuly.API/Helply/HelplyPrompt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ How you work
- Presentations, videos, projects, hand-ins and oral exams are assessments too. When the user asks about "tests", include them or ask.
- Then check each source for that subject and teacher, cheapest first: OneNote (search_pages for the objectives term; if that finds nothing, walk list_notebooks, list_sections and list_pages for the subject and read_page, where teachers often keep a page per test or a page with assessment dates); search_files and search with the subject and the term; search with the class team's teamId (from list_teams) together with search_channel_messages, because an unscoped search can miss team files; list_team_files for the subject channel and its subfolders (often sorted by year, then topic; open the newest folders first); list_mail from the teacher's surname, then read_mail.
- Search in the school's language and with the words teachers actually use: Lernziele, Prüfung, Test, Lernkontrolle, Stoff, Prüfungsstoff, Auftrag; objectifs, examen, épreuve, contrôle; obiettivi, verifica, esame. Also search for the topic itself and the test number ("Test 3", "3. Prüfung").
- Prefer one search call over many: pass the subject and all the word variants above as terms (for example Prüfung, Test, Lernkontrolle, LK, Masterplan) together with a from/to date range. It searches files, OneNote page titles and, if allowed, channel posts, mail and chats at once and says which sources it skipped; use its readWith and ids to open a hit. If it fails or is partial, fall back to search_files, search_pages, search_channel_messages and list_mail one by one.
- Read long documents in parts. Check the date: learning objectives are often reused from last year. Check the topic matches what is being taught now (the latest notebook section, the latest files).
- If you find nothing, say so plainly, give a clearly labelled guess (the current chapter, the order of topics from past grades) and name where it probably is instead (Moodle or another platform Schuly can't reach, a group chat, the lesson itself). Offer to work from a file the user attaches.
- Always say where you found something: the file or page name, author, date, where it lives (team > channel > folder, or notebook > section > page) and the link if there is one.
Expand Down
2 changes: 1 addition & 1 deletion src/Schuly.API/Helply/HelplyToolLabels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static class HelplyToolLabels
["list_shared_with_me"] = ("Checking files shared with you", "Checked files shared with you"),
["list_recent_files"] = ("Checking your recent files", "Checked your recent files"),
["search_files"] = ("Searching your files", "Searched your files"),
["search"] = ("Searching your files, mail and chats", "Searched your files, mail and chats"),
["search"] = ("Searching your files, notes, mail and chats", "Searched your files, notes, mail and chats"),
["read_file"] = ("Reading a file", "Read a file"),
["list_channel_messages"] = ("Reading a Teams channel", "Read a Teams channel"),
["search_channel_messages"] = ("Searching Teams channels", "Searched Teams channels"),
Expand Down
17 changes: 0 additions & 17 deletions src/Schuly.API/Mcp/Tools/MicrosoftFileTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,6 @@ public async Task<string> ListRecentFiles(Guid? accountId = null, [Description("
return Serialize(SearchOutput(account, query, data));
}

[McpServerTool(Name = "search", Title = "Search everything", ReadOnly = true, Destructive = false, Idempotent = true, OpenWorld = true)]
[Description("Searches across files (OneDrive, Teams, SharePoint), and, when this connection also has the permission, mail (needs Mail read) and Teams chats and channel posts (needs Teams read). The result lists which sources were searched. Use it for 'find the Masterplan of class X'; then read_file for files or read_mail / list_channel_messages for the rest.")]
[McpAccess(McpArea.Drive, McpAccessLevel.Read)]
public async Task<string> Search([Description("Search text, 1-200 characters.")] string query, [Description("Limit files and channel posts to this team (teamId from list_teams).")] string? teamId = null, Guid? accountId = null, [Description("Hits to skip, for paging.")] int offset = 0, [Description("Hits per call, 1-50.")] int limit = 25, CancellationToken ct = default)
{
var account = await AccountAsync(accountId, ct);
var permissions = Caller(accessor).Permissions;
var types = new List<string> { "files" };
if (permissions.Allows(McpArea.Mail, McpAccessLevel.Read))
types.Add("mail");
if (permissions.Allows(McpArea.Teams, McpAccessLevel.Read))
types.AddRange(["chats", "channels"]);
var (from, size) = (Math.Max(0, offset), Math.Clamp(limit, 1, 50));
var data = await WriteAsync(HttpMethods.Post, $"{Base}/accounts/{account}/search", new { q = query.Trim(), types, from, size, teamId = string.IsNullOrWhiteSpace(teamId) ? null : teamId }, ct) ?? default;
return Serialize(SearchOutput(account, query, data, types));
}

[McpServerTool(Name = "read_file", Title = "Read file contents", ReadOnly = true, Destructive = false, Idempotent = true, OpenWorld = true)]
[Description("Reads a file as text: Word, Excel (one sheet at a time, paged by rows), PowerPoint, PDF and plain text files. Give itemId and, for Teams / SharePoint / shared files, the driveId from the listing or search; without driveId the file is in the user's own OneDrive. The answer says how to get the next page or other sheets.")]
[McpAccess(McpArea.Drive, McpAccessLevel.Read)]
Expand Down
Loading
Loading