| Plugin ID | gorang3.worklog |
|---|---|
| Třída | GoranG3WorklogPlugin |
| Rozhraní | IWorklogUploadPlugin |
| Autentizace | Entra ID (MSAL device code flow) |
| Protokol | MCP (Model Context Protocol) |
Plugin odesílá worklogy do interního systému Goran G3 přes jeho MCP server. Cílová skupina: zaměstnanci organizací používajících Goran G3 jako výkaznictví.
| Pole | Typ | Povinné | Default | Popis |
|---|---|---|---|---|
GoranBaseUrl |
Url |
✅ | — | Base URL Goran G3 MCP serveru, např. https://moonfish-g3.goran.cz |
ProjectCode |
Text |
✅ | — | Kód projektu, na který se worklog účtuje (např. 000.GOR) |
ProjectPhaseCode |
Text |
❌ | — | Fáze projektu (volitelné — některé projekty fáze nemají) |
Tags |
Text |
❌ | — | Čárkami oddělený seznam tagů |
EntraClientId |
Text |
✅ | — | Client ID aplikace registrované v Entra |
EntraTenantId |
Text |
✅ | — | Tenant ID |
EntraScopes |
Text |
✅ | — | API scope pro přístup na Goran G3 MCP, ve tvaru api://{goran-api-client-id}/Mcp.Access. Konkrétní hodnotu získáš od administrátora Goran G3. |
Postup je shodný s Office 365 Calendar pluginem, ale scopes jsou jiné — místo Microsoft Graph používáš API exposed by Goran G3 (delegated permissions na custom API v Entra). Získej konkrétní scopes od Goran G3 administrátora své organizace.
Pokud tvoje organizace má centrální app registraci pro Goran G3 (sdílenou aplikaci pro všechny interní klienty), stačí od IT dostat:
EntraClientIdEntraTenantIdEntraScopes(pokud se liší od defaultu)
Stejně jako Office 365 Calendar plugin. Po vyplnění konfigurace:
- Settings → Plugins → Goran G3 → Test connection.
- Plugin zavolá
ITokenProviderFactory.CreateAsync(tenantId, clientId, scopes). - Silent token cache se zkusí první; pokud cache prázdná / expirovaná, spustí se device code flow.
- Progress dialog ukáže user code + URL
https://microsoft.com/devicelogin. - Po úspěšném přihlášení plugin otestuje MCP connection.
Tokeny jsou v šifrované cache (v release buildu typicky %LocalAppData%\WorkTracker\keys\), obnova přes refresh token je automatická. V non‑Production prostředích používá WorkTrackerPaths suffix podle DOTNET_ENVIRONMENT, takže při dotnet run může být reálná cesta %LocalAppData%\WorkTracker_Development\keys\. Při ručním mazání cache (reset) smaž složku odpovídající aktuálnímu prostředí.
GoranG3WorklogPlugin používá McpClient z NuGet balíčku ModelContextProtocol, který:
- Hostí HTTP spojení na
{GoranBaseUrl}/mcp. - Všechny requesty procházejí
TokenInjectingHandler— vlastníDelegatingHandler, který:- Před každým requestem zavolá
_tokenProvider.AcquireTokenSilentAsync. - Pokud silent auth selže a token je
null, handler vyhodíInvalidOperationException— žádný interaktivní fallback uvnitř handleru neprobíhá. Uživatel musí spustit Test connection v Settings, která interaktivní device code flow rozběhne explicitně. - Do
Authorizationhlavičky vložíBearer {token}.
- Před každým requestem zavolá
- Volá MCP tools (snake_case název, argumenty v JSON objektu):
create_my_timesheet_item— upload jednoho záznamu.submit_my_timesheet— finální submit sady záznamů (přiUploadWorklogsAsync).get_my_timesheet_items_list— query existujících worklogů (GetWorklogsAsync,WorklogExistsAsync).
Plugin předá nástroji tento dictionary (klíče snake_case podle MCP schématu Goran G3):
{
"project_code": "000.GOR",
"project_phase_code": "SP",
"date": "2026-04-09",
"start_time": "09:00",
"duration_minutes": 60,
"text": "PROJ-123 Bug fix v autentizaci",
"tags": ["dev", "bugfix"],
"external_id": 123
}Mapování polí:
text— kombinace ticketu (pokud je) a popisu zPluginWorklogEntry(plugin používá interníBuildTexthelper).external_id— plugin se pokusí zPluginWorklogEntry.TicketIdvyparsovat numerické ID (vizParseExternalId). Pokud ticket nelze převést na číslo, pole se do argumentů nepřidá.project_phase_code— přidá se jen tehdy, když je poleProjectPhaseCodev konfiguraci vyplněné.tags— přidá se jen tehdy, když je poleTagsvyplněné; rozparsuje se split po,+ trim.
- Získání tokenu — plugin nejdřív zkusí
AcquireTokenSilentAsync, a pokud je cache prázdná/expirovaná, přejde naAcquireTokenInteractiveAsync(device code flow). Průběh reportuje doIProgress<string>. - Připojení k MCP — naváže spojení na
{GoranBaseUrl}/mcp. - Ověření dostupnosti required toolu — zavolá MCP
list_toolsa kontroluje, jestli server nabízícreate_my_timesheet_item. - Reportuje stavy typu: „Získávám token…”, „Připojuji k MCP…”, „Ověřuji dostupné tools…”, „OK”.
Pozor:
Test connectionaktuálně neprovádí validaciProjectCodeaniProjectPhaseCode. Jejich správnost se projeví až při skutečném uploadu worklogu (kdy MCP server případně vrátí chybu).
UploadWorklogsAsyncpřijme kolekciPluginWorklogEntryaWorklogSubmissionMode.- Pro každý záznam:
- Sestaví argumenty pro MCP tool
create_my_timesheet_item(snake_case pole viz výše). - Pošle přes
_mcpClient.CallToolAsync("create_my_timesheet_item", arguments, ct). - Pokud server vrátí success, inkrementuje
SuccessfulEntries. - Pokud fail, přidá do
ErrorssErrorMessageaWorklog.
- Sestaví argumenty pro MCP tool
- Po zpracování všech záznamů plugin zavolá
submit_my_timesheetpro finální uložení celé sady. - Vrátí
PluginResult<WorklogSubmissionResult>.
Plugin inzeruje pouze SupportedModes = Timed (výchozí z WorklogUploadPluginBase). Goran G3 MCP vyžaduje pro každý záznam volání create_my_timesheet_item s reálným start_time a pak finální submit_my_timesheet — tato sekvence neumožňuje agregaci více záznamů do jednoho per kód+popis. Submission dialog proto GoranG3 v Agregovaném módu v dropdownu neukáže.
Retry — plugin neretryne automaticky. Pokud dojde k síťové chybě, záznam skončí v Errors a uživatel může kliknout Retry failed v dialogu.
Důvod: Goran G3 MCP zatím nemá idempotency token, takže retry by mohl vytvořit duplicitní záznamy. Jakmile bude idempotency, retry logika se doplní.
Plugin podporuje i čtení existujících worklogů přes MCP tool get_my_timesheet_items_list — to aplikaci dovoluje zkontrolovat před uploadem, jestli už záznam existuje (a tedy neposílat duplicitu).
GetWorklogsAsync(startDate, endDate):
- Zavolá
get_my_timesheet_items_lists argumentydate_fromadate_tove formátuyyyy-MM-dd. - Zmapuje vrácené timesheet položky zpět na
PluginWorklogEntry.
WorklogExistsAsync(worklog):
- Volá
GetWorklogsAsync(worklog.StartTime.Date, worklog.StartTime.Date). - Hledá záznam ve stejný den s přibližně stejným
startTime(tolerance< 1 min) a stejnouDurationMinutes. ticketIdse při této kontrole duplicit neporovnává — v praxi tedy dva worklogy stejné délky začínající ve stejnou minutu dostanou jeden jako duplicitu, i když mají jiný ticket.- Vrací
true/false.
| Symptom | Příčina | Řešení |
|---|---|---|
Unable to connect to MCP endpoint |
Neplatná GoranBaseUrl nebo výpadek serveru |
Zkontroluj URL a dostupnost serveru |
401 Unauthorized i po přihlášení |
Token má špatné scopes | Zkontroluj EntraScopes s IT |
Project code 000.GOR not found |
Projekt neexistuje, nebo nemáš k němu přístup | Kontaktuj project managera Goran G3 |
| Device code flow timeoutuje | Uživatel nepotvrdil login do cca 15 minut | Klikni Test connection znovu |
Duplicate worklog detected |
Retry po úspěšném uploadu — server vrací konflikt | Nech aktuální stav, worklog je uložený |
Stejné jako Office 365 Calendar: smaž soubory v %LocalAppData%\WorkTracker\keys\ pro reset MSAL cache.
Pozor — dvě klíčová omezení pro CLI:
- CLI pluginy neenable-uje.
WorkTracker.CLIvoláInitializePluginsAsyncbez enable mapy, takže i správně vyplněnéappsettings.jsonpluginy nespustí. Plugin konfiguruj v GUI (Nastavení → Pluginy → Goran G3).- GoranG3 vyžaduje předchozí GUI login.
TokenInjectingHandlerv pluginu používá pouzeAcquireTokenSilentAsync— při uploadu se nikdy neotevře device code flow. Musíš nejdřív spustit Test connection v GUI Settings, která interaktivně získá token a uloží ho do cache. Teprve pak bude plugin fungovat v jakémkoli hostu.Schéma níže je referenční a uplatní se pouze ve vlastním hostu, který předá
enabledPluginsmapu doInitializePluginsAsync.
{
"Plugins": {
"gorang3.worklog": {
"GoranBaseUrl": "https://moonfish-g3.goran.cz",
"ProjectCode": "000.GOR",
"EntraClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"EntraTenantId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"EntraScopes": "api://{goran-api-client-id}/Mcp.Access",
"Tags": "dev"
}
}
}Ve vlastním hostu, který explicitně volá TestConnectionAsync a reportuje progress do konzole, může device code flow fungovat tak, že kód a URL vypíše do terminálu jako stdout. To ale neplatí pro běžné WorkTracker.CLI, kde pluginy nejsou povolené a upload pipeline stejně neumí spustit interaktivní auth.