chore: keep widget sources only
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "shd-public",
|
||||||
|
"interface": {
|
||||||
|
"displayName": "SHD Public Plugins"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "shd-mcp-plugin",
|
||||||
|
"source": {
|
||||||
|
"source": "local",
|
||||||
|
"path": "./plugins/shd-mcp-plugin"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"installation": "AVAILABLE",
|
||||||
|
"authentication": "ON_INSTALL"
|
||||||
|
},
|
||||||
|
"category": "Productivity"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.log
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# MCP widget runtime artifacts are generated from the sources before serving.
|
||||||
|
/plugins/shd-mcp-plugin/widgets/*/v1/
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# SHD MCP in ChatGPT
|
||||||
|
|
||||||
|
This file describes the user-side connection. The repository cannot create an
|
||||||
|
app inside another person’s ChatGPT account and never stores OAuth tokens.
|
||||||
|
|
||||||
|
## Custom app
|
||||||
|
|
||||||
|
1. Open ChatGPT settings and enable Developer mode for Apps/Connectors.
|
||||||
|
2. Create a custom app with this MCP endpoint:
|
||||||
|
|
||||||
|
`https://shd.xyz.su/mcp`
|
||||||
|
|
||||||
|
3. Complete the SHD OAuth sign-in and consent screen.
|
||||||
|
4. Start a new chat or refresh the app connection.
|
||||||
|
5. Test with: `Покажи активные проекты с датой завершения и статусом.`
|
||||||
|
|
||||||
|
The server publishes OAuth 2.1 discovery for the root resource and supports
|
||||||
|
ChatGPT Client ID Metadata Documents (CIMD), PKCE S256 and the
|
||||||
|
`private_key_jwt` token-endpoint authentication method. Keep the app URL as
|
||||||
|
`https://shd.xyz.su/mcp`; the retired `/mcp/system-data` resource is not a
|
||||||
|
valid replacement for the root MCP resource.
|
||||||
|
|
||||||
|
The expected flow is:
|
||||||
|
|
||||||
|
1. ChatGPT calls `shd_list_projects` with `archived: false`.
|
||||||
|
2. ChatGPT calls the corresponding module render tool after the list tool
|
||||||
|
before the final response when MCP Apps UI is supported:
|
||||||
|
`shd_render_projects_widget`, `shd_render_documents_widget`,
|
||||||
|
`shd_render_tasks_widget`, `shd_render_finance_widget`,
|
||||||
|
`shd_render_crm_widget`, `shd_render_discussions_widget`,
|
||||||
|
`shd_render_notifications_widget`, `shd_render_scheduling_widget`,
|
||||||
|
`shd_render_inventory_widget`, `shd_render_agents_widget`,
|
||||||
|
`shd_render_status_page_widget`, `shd_render_airbase_widget`,
|
||||||
|
`shd_render_notes_widget`, `shd_render_project_overview_widget`,
|
||||||
|
`shd_render_files_widget`, `shd_render_proposals_widget`,
|
||||||
|
`shd_render_activity_widget`, `shd_render_organizations_widget`,
|
||||||
|
`shd_render_gitea_widget`, `shd_render_schemes_widget`, `shd_render_task_kanban_widget`,
|
||||||
|
`shd_render_team_workload_widget`, `shd_render_project_timeline_widget`,
|
||||||
|
`shd_render_crm_funnel_widget`, `shd_render_finance_dashboard_widget`,
|
||||||
|
`shd_render_proposal_approvals_widget`, `shd_render_documents_completeness_widget`,
|
||||||
|
`shd_render_agents_health_widget`, `shd_render_schedule_calendar_widget`,
|
||||||
|
`shd_render_acl_matrix_widget`, `shd_render_inventory_warnings_widget`,
|
||||||
|
`shd_render_gitea_board_widget`, `shd_render_schemes_progress_widget`,
|
||||||
|
`shd_render_notes_tree_widget` or `shd_render_airbase_preview_widget`.
|
||||||
|
3. The selected widget renders a register, can call the matching detail tool for
|
||||||
|
one selected row and exposes controlled write actions when declared by the
|
||||||
|
render metadata.
|
||||||
|
4. For controller requests, use the explicit controller data→render pair:
|
||||||
|
`shd_get_controller_source_inventory` → `shd_render_controller_devices_widget`,
|
||||||
|
`shd_get_home_live_state` → `shd_render_controller_live_state_widget`,
|
||||||
|
`shd_get_controller_source_history` → `shd_render_controller_history_widget`,
|
||||||
|
or `shd_get_controller_state_statistics` →
|
||||||
|
`shd_render_controller_statistics_widget`. Pass the complete data result and
|
||||||
|
preserve `project`, `controller` and `item_id` in `meta.sourceArgs`.
|
||||||
|
|
||||||
|
The widget resources are versioned at `ui://shd/active-projects/v1.html`,
|
||||||
|
`ui://shd/documents/v1.html`, `ui://shd/tasks/v1.html`,
|
||||||
|
`ui://shd/finance/v1.html`, `ui://shd/crm/v1.html`,
|
||||||
|
`ui://shd/discussions/v1.html`, `ui://shd/notifications/v1.html`,
|
||||||
|
`ui://shd/scheduling/v1.html`, `ui://shd/inventory/v1.html`,
|
||||||
|
`ui://shd/agents/v1.html`, `ui://shd/status-page/v1.html`,
|
||||||
|
`ui://shd/controller-observability/v1.html`,
|
||||||
|
`ui://shd/airbase/v1.html`, `ui://shd/notes/v1.html`,
|
||||||
|
`ui://shd/project-overview/v1.html`, `ui://shd/files/v1.html`,
|
||||||
|
`ui://shd/proposals/v1.html`,
|
||||||
|
`ui://shd/activity/v1.html`, `ui://shd/organizations/v1.html`,
|
||||||
|
`ui://shd/gitea/v1.html`, `ui://shd/pdf-scheme-editor/v1.html`,
|
||||||
|
`ui://shd/task-kanban/v1.html`, `ui://shd/team-workload/v1.html`,
|
||||||
|
`ui://shd/project-timeline/v1.html`, `ui://shd/crm-funnel/v1.html`,
|
||||||
|
`ui://shd/finance-dashboard/v1.html`, `ui://shd/proposal-approvals/v1.html`,
|
||||||
|
`ui://shd/documents-completeness/v1.html`, `ui://shd/agents-health/v1.html`,
|
||||||
|
`ui://shd/schedule-calendar/v1.html`, `ui://shd/acl-matrix/v1.html`,
|
||||||
|
`ui://shd/inventory-warnings/v1.html`, `ui://shd/gitea-board/v1.html`,
|
||||||
|
`ui://shd/pdf-scheme-editor-progress/v1.html`, `ui://shd/notes-tree/v1.html` and
|
||||||
|
`ui://shd/airbase-preview/v1.html`.
|
||||||
|
|
||||||
|
The MCP tools remain usable without the widget. A failed OAuth connection is
|
||||||
|
an account/endpoint configuration problem, not a missing token in this repo.
|
||||||
|
|
||||||
|
## Public directory
|
||||||
|
|
||||||
|
Directory submission is a separate optional release step. It is needed for
|
||||||
|
public discovery and reviewed installation, not for internal use or a custom
|
||||||
|
app added manually in ChatGPT. Before submission, the owner must provide the
|
||||||
|
production HTTPS endpoint, OAuth metadata, privacy/support URLs, accurate app
|
||||||
|
metadata, test prompts and any review credentials requested by OpenAI.
|
||||||
|
|
||||||
|
Use the official [submission guide](https://developers.openai.com/plugins/deploy/submission)
|
||||||
|
and [plugin guidelines](https://developers.openai.com/plugins/app-guidelines).
|
||||||
|
|
||||||
|
## License choice
|
||||||
|
|
||||||
|
This repository currently uses the proprietary SHD MCP Plugin License. Public
|
||||||
|
visibility means the source can be viewed; it does not grant permission to
|
||||||
|
redistribute, resell, modify or use it outside an authorized SHD account.
|
||||||
|
|
||||||
|
MIT would grant anyone permission to use, copy, modify, publish, sublicense and
|
||||||
|
sell the plugin, including forks, while retaining only the copyright notice and
|
||||||
|
license text. Switching to MIT is a product/legal decision and is not done
|
||||||
|
implicitly by publishing the repository.
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
SHD MCP PLUGIN LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2026 SHD
|
||||||
|
|
||||||
|
This license applies to the SHD MCP Plugin package, its Skills, manifests,
|
||||||
|
documentation and SHD branding in this repository. The SHD MCP service and
|
||||||
|
its backend are separate systems and remain subject to their own terms.
|
||||||
|
|
||||||
|
1. Permission
|
||||||
|
|
||||||
|
SHD grants a limited, non-exclusive, non-transferable permission to install and
|
||||||
|
use an unmodified copy of this plugin with an authorized SHD account and a
|
||||||
|
compatible MCP client.
|
||||||
|
|
||||||
|
2. Restrictions
|
||||||
|
|
||||||
|
Unless SHD gives prior written permission, you may not sell, sublicense,
|
||||||
|
redistribute, publish modified versions of, remove notices from, or use this
|
||||||
|
package and its branding to provide a competing service. Public access to this
|
||||||
|
repository does not transfer copyright, trademark or other intellectual
|
||||||
|
property rights.
|
||||||
|
|
||||||
|
3. No credentials or access grant
|
||||||
|
|
||||||
|
This package contains no access token and grants no access to SHD data. Access
|
||||||
|
is controlled by SHD authentication, organization membership and server-side
|
||||||
|
permissions.
|
||||||
|
|
||||||
|
4. Warranty and liability
|
||||||
|
|
||||||
|
The package is provided "as is" to the maximum extent permitted by law,
|
||||||
|
without warranties of any kind. SHD is not liable for indirect, incidental or
|
||||||
|
consequential damages arising from its use.
|
||||||
|
|
||||||
|
5. Third-party materials
|
||||||
|
|
||||||
|
Third-party software and assets, if any, remain subject to their own licenses.
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
# SHD MCP Plugin
|
||||||
|
|
||||||
|
Public installable plugin package for SHD project workflows. It combines Codex
|
||||||
|
Skills with the authenticated SHD MCP server and keeps business logic, ACLs,
|
||||||
|
OAuth and persisted data in SHD itself.
|
||||||
|
|
||||||
|
## What the user installs
|
||||||
|
|
||||||
|
The package contains:
|
||||||
|
|
||||||
|
- `.codex-plugin/plugin.json` — plugin metadata;
|
||||||
|
- `.mcp.json` — the official SHD Streamable HTTP MCP endpoint;
|
||||||
|
- `skills/` — routing, project, ProjectBase, task, file, finance, CRM,
|
||||||
|
analytics, estimates, scheduling, entity resolution, Wiki, discussions,
|
||||||
|
documents, organizations/ACL, notifications, inventory, agents, status-page,
|
||||||
|
realtime/activity, Terms/contracts and Gitea workflows;
|
||||||
|
- `assets/` — plugin branding;
|
||||||
|
- `widgets/` — versioned MCP Apps resources with manifest, checksum and
|
||||||
|
provenance metadata;
|
||||||
|
- `.agents/plugins/marketplace.json` — a ready local marketplace entry.
|
||||||
|
|
||||||
|
It does not contain the SHD Laravel application, database code or credentials.
|
||||||
|
|
||||||
|
## Install in Codex
|
||||||
|
|
||||||
|
Clone this repository and register its marketplace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/bulava92/shd-mcp-plugin.git
|
||||||
|
cd shd-mcp-plugin
|
||||||
|
codex plugin marketplace add "$PWD"
|
||||||
|
codex plugin add shd-mcp-plugin@shd-public
|
||||||
|
```
|
||||||
|
|
||||||
|
After installation, start a new Codex thread so the plugin Skills are loaded.
|
||||||
|
|
||||||
|
## ChatGPT custom app
|
||||||
|
|
||||||
|
ChatGPT uses the same SHD MCP server connection, but it is added separately in
|
||||||
|
the ChatGPT account: open Settings → Apps/Connectors → Developer mode, create
|
||||||
|
a custom app and set the MCP URL to `https://shd.xyz.su/mcp`. Complete the SHD
|
||||||
|
OAuth flow and refresh the app after server metadata changes.
|
||||||
|
|
||||||
|
The active-projects result can render an inline MCP Apps widget. It is a small
|
||||||
|
dashboard with:
|
||||||
|
|
||||||
|
- `Проект → Дата завершения → Статус`;
|
||||||
|
- sorting by the nearest deadline;
|
||||||
|
- local search by project name/code;
|
||||||
|
- a refresh action, a project-details panel and controlled project actions.
|
||||||
|
|
||||||
|
If the host does not support MCP Apps UI, the data tool and the plain Markdown
|
||||||
|
table remain fully usable.
|
||||||
|
|
||||||
|
The backend serves the generated widget artifact at
|
||||||
|
`ui://shd/active-projects/v1.html`. Generate the ignored runtime files before
|
||||||
|
starting the backend:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node scripts/generate-widget-artifacts.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
The source is kept in `widgets/active-projects/source.html` and
|
||||||
|
`widgets/active-projects/widget.json`; generated `v1/` files are not committed.
|
||||||
|
|
||||||
|
The same data/render contract is available for Documents, Tasks,
|
||||||
|
Finance, CRM, Discussions, Notifications, Scheduling, Inventory, Agents,
|
||||||
|
Status Page, AirBase, Notes, Project overview, Files, Proposals, Activity,
|
||||||
|
Organizations and access, Gitea Issues and PDF Scheme Editor:
|
||||||
|
|
||||||
|
Controller observability uses one parameterized resource for device inventory,
|
||||||
|
current state, server-computed statistics, source history, cross-controller
|
||||||
|
comparisons, expert settings, complete project topology, telemetry health and
|
||||||
|
source audit:
|
||||||
|
|
||||||
|
- `shd_render_controller_history_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_statistics_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_devices_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_live_state_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_compare_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_settings_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_topology_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_telemetry_health_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_audit_widget` → `ui://shd/controller-observability/v1.html`.
|
||||||
|
|
||||||
|
The controller topology keeps Loxone/WB source controllers separate from
|
||||||
|
Raspberry Pi and mini PC controller rows without source inventory; it does not
|
||||||
|
substitute the separate agent register for the project controller list.
|
||||||
|
The agent overview is the inverse boundary: it lists agents/runtime access and
|
||||||
|
does not become a controller list merely because an agent runs on a controller.
|
||||||
|
|
||||||
|
- `shd_render_documents_widget` → `ui://shd/documents/v1.html`;
|
||||||
|
- `shd_render_tasks_widget` → `ui://shd/tasks/v1.html`;
|
||||||
|
- `shd_render_finance_widget` → `ui://shd/finance/v1.html`;
|
||||||
|
- `shd_render_crm_widget` → `ui://shd/crm/v1.html`;
|
||||||
|
- `shd_render_discussions_widget` → `ui://shd/discussions/v1.html`;
|
||||||
|
- `shd_render_notifications_widget` → `ui://shd/notifications/v1.html`;
|
||||||
|
- `shd_render_scheduling_widget` → `ui://shd/scheduling/v1.html`;
|
||||||
|
- `shd_render_inventory_widget` → `ui://shd/inventory/v1.html`;
|
||||||
|
- `shd_render_agents_widget` → `ui://shd/agents/v1.html`;
|
||||||
|
- `shd_render_status_page_widget` → `ui://shd/status-page/v1.html`;
|
||||||
|
- `shd_render_airbase_widget` → `ui://shd/airbase/v1.html`;
|
||||||
|
- `shd_render_notes_widget` → `ui://shd/notes/v1.html`;
|
||||||
|
- `shd_render_project_overview_widget` → `ui://shd/project-overview/v1.html`;
|
||||||
|
- `shd_render_files_widget` → `ui://shd/files/v1.html`;
|
||||||
|
- `shd_render_proposals_widget` → `ui://shd/proposals/v1.html`;
|
||||||
|
- `shd_render_activity_widget` → `ui://shd/activity/v1.html`;
|
||||||
|
- `shd_render_organizations_widget` → `ui://shd/organizations/v1.html`;
|
||||||
|
- `shd_render_gitea_widget` → `ui://shd/gitea/v1.html`;
|
||||||
|
- `shd_render_schemes_widget` → `ui://shd/pdf-scheme-editor/v1.html`;
|
||||||
|
- `shd_render_task_kanban_widget` → `ui://shd/task-kanban/v1.html`;
|
||||||
|
- `shd_render_team_workload_widget` → `ui://shd/team-workload/v1.html`;
|
||||||
|
- `shd_render_project_timeline_widget` → `ui://shd/project-timeline/v1.html`;
|
||||||
|
- `shd_render_crm_funnel_widget` → `ui://shd/crm-funnel/v1.html`;
|
||||||
|
- `shd_render_finance_dashboard_widget` → `ui://shd/finance-dashboard/v1.html`;
|
||||||
|
- `shd_render_proposal_approvals_widget` → `ui://shd/proposal-approvals/v1.html`;
|
||||||
|
- `shd_render_documents_completeness_widget` → `ui://shd/documents-completeness/v1.html`;
|
||||||
|
- `shd_render_agents_health_widget` → `ui://shd/agents-health/v1.html`;
|
||||||
|
- `shd_render_schedule_calendar_widget` → `ui://shd/schedule-calendar/v1.html`;
|
||||||
|
- `shd_render_acl_matrix_widget` → `ui://shd/acl-matrix/v1.html`;
|
||||||
|
- `shd_render_inventory_warnings_widget` → `ui://shd/inventory-warnings/v1.html`;
|
||||||
|
- `shd_render_gitea_board_widget` → `ui://shd/gitea-board/v1.html`;
|
||||||
|
- `shd_render_schemes_progress_widget` → `ui://shd/pdf-scheme-editor-progress/v1.html`;
|
||||||
|
- `shd_render_notes_tree_widget` → `ui://shd/notes-tree/v1.html`;
|
||||||
|
- `shd_render_airbase_preview_widget` → `ui://shd/airbase-preview/v1.html`.
|
||||||
|
|
||||||
|
Each widget accepts normalized records from its list tool, keeps the plain
|
||||||
|
structured response as a fallback and uses the MCP Apps bridge before the
|
||||||
|
`window.openai` compatibility API.
|
||||||
|
|
||||||
|
The official OpenAI directory is optional. It is useful for public discovery,
|
||||||
|
one-click installation and review of a public app; it is not required for an
|
||||||
|
internal team or a manually added ChatGPT custom app. See the official
|
||||||
|
[MCP server guide](https://developers.openai.com/plugins/build/mcp-server),
|
||||||
|
[UI guide](https://developers.openai.com/plugins/build/chatgpt-ui), and
|
||||||
|
[submission guide](https://developers.openai.com/plugins/deploy/submission)
|
||||||
|
when public listing is the goal.
|
||||||
|
|
||||||
|
## Connect SHD
|
||||||
|
|
||||||
|
Installation and authorization are separate steps:
|
||||||
|
|
||||||
|
1. The client reads `.mcp.json` and opens the SHD OAuth flow.
|
||||||
|
2. The user signs in to SHD and approves the requested MCP connection.
|
||||||
|
3. SHD issues the client an authorization; the client stores it securely.
|
||||||
|
4. Every tool call is checked again by SHD for organization, project and
|
||||||
|
module permissions.
|
||||||
|
|
||||||
|
The user does not need to put a token in this repository. A personal access
|
||||||
|
token may be used only if the selected MCP client and SHD deployment
|
||||||
|
explicitly support it; enter it in that client's secure connection settings,
|
||||||
|
never in `.mcp.json`, Skills or an issue.
|
||||||
|
|
||||||
|
SHD OAuth supports ChatGPT CIMD clients with PKCE S256 and
|
||||||
|
`private_key_jwt` client authentication. The server validates the client
|
||||||
|
assertion against ChatGPT's published JWKS; the plugin package never stores
|
||||||
|
the assertion, access token or refresh token.
|
||||||
|
|
||||||
|
For ChatGPT or another MCP client without Codex plugin installation, add the
|
||||||
|
same HTTPS endpoint as a custom MCP app and complete OAuth there. The public
|
||||||
|
repository packages the workflows; it does not grant access to the server.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **Plugin installs but tools are unavailable:** reconnect SHD OAuth and start
|
||||||
|
a new thread.
|
||||||
|
- **Projects are missing:** the SHD account or organization lacks access, or
|
||||||
|
the project is archived; verify permissions in SHD.
|
||||||
|
- **A write is rejected:** the server ACL, required role, version or conflict
|
||||||
|
check rejected it. Do not bypass the error with a different token.
|
||||||
|
- **A module is not covered by a dedicated skill:** use `shd-routing`; the MCP
|
||||||
|
server still exposes the current authenticated tool catalog, while skills
|
||||||
|
provide focused workflow and safety guidance for common module operations.
|
||||||
|
- **Another SHD installation is required:** use its approved MCP URL and its
|
||||||
|
OAuth resource in the client's secure connection configuration.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Keep changes inside the plugin package. The authenticated MCP server, OAuth
|
||||||
|
deployment and runtime verification remain private server-side concerns. Do not
|
||||||
|
place credentials, private host details or backend implementation in Skills or
|
||||||
|
widget assets.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The repository is public source, but the plugin and SHD branding are distributed
|
||||||
|
under the proprietary [SHD MCP Plugin License](LICENSE). Public visibility does
|
||||||
|
not grant rights to redistribute, resell, modify or reuse the package outside
|
||||||
|
the permitted SHD use.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "shd-mcp-plugin",
|
||||||
|
"version": "0.5.1",
|
||||||
|
"description": "Ready-to-install SHD workflows backed by the authenticated SHD MCP server.",
|
||||||
|
"author": {
|
||||||
|
"name": "SHD",
|
||||||
|
"url": "https://shd.xyz.su"
|
||||||
|
},
|
||||||
|
"homepage": "https://shd.xyz.su",
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
|
"license": "Proprietary",
|
||||||
|
"keywords": [
|
||||||
|
"SHD",
|
||||||
|
"projects",
|
||||||
|
"ProjectBase",
|
||||||
|
"tasks",
|
||||||
|
"finance",
|
||||||
|
"CRM",
|
||||||
|
"documents",
|
||||||
|
"inventory",
|
||||||
|
"organizations",
|
||||||
|
"notifications",
|
||||||
|
"realtime",
|
||||||
|
"Gitea",
|
||||||
|
"MCP"
|
||||||
|
],
|
||||||
|
"skills": "./skills/",
|
||||||
|
"interface": {
|
||||||
|
"displayName": "SHD MCP",
|
||||||
|
"shortDescription": "SHD projects and operations",
|
||||||
|
"longDescription": "Installable SHD MCP workflows for projects, ProjectBase, tasks, files, finance, CRM, documents, access control, inventory, notifications, realtime, status monitoring and linked Gitea operations.",
|
||||||
|
"developerName": "SHD",
|
||||||
|
"category": "Productivity",
|
||||||
|
"capabilities": [
|
||||||
|
"Read",
|
||||||
|
"Write"
|
||||||
|
],
|
||||||
|
"websiteURL": "https://shd.xyz.su",
|
||||||
|
"brandColor": "#1E88E5",
|
||||||
|
"composerIcon": "./assets/logo.png",
|
||||||
|
"logo": "./assets/logo.png",
|
||||||
|
"defaultPrompt": [
|
||||||
|
"Покажи активные проекты с датой завершения и статусом.",
|
||||||
|
"Сделай сводку по проекту и его рискам.",
|
||||||
|
"Проверь ProjectBase без изменения данных."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mcpServers": "./.mcp.json"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"shd": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://shd.xyz.su/mcp",
|
||||||
|
"oauth_resource": "https://shd.xyz.su/mcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# SHD MCP plugin package
|
||||||
|
|
||||||
|
This directory is the installable Codex plugin. It packages reusable SHD
|
||||||
|
workflow Skills and the official SHD MCP connection; it does not contain the
|
||||||
|
SHD backend or a second MCP server.
|
||||||
|
|
||||||
|
The plugin requires an SHD account authorized for the requested projects and
|
||||||
|
modules. Skills do not grant permissions or bypass server-side ACLs.
|
||||||
|
|
||||||
|
## Included workflows
|
||||||
|
|
||||||
|
- active projects with completion dates and statuses;
|
||||||
|
- project status and risk summaries;
|
||||||
|
- ProjectBase schema and data-quality audits;
|
||||||
|
- guarded task workflows;
|
||||||
|
- project file inspection and actions;
|
||||||
|
- finance and CRM read/audit workflows;
|
||||||
|
- finance account, balance, payment and settlement audits;
|
||||||
|
- analytics, comparisons, reports and task/proposal metrics;
|
||||||
|
- entity resolution and duplicate-candidate analysis;
|
||||||
|
- estimates, proposals and estimate-document-contract workflows;
|
||||||
|
- scheduling events, slots and booking positions;
|
||||||
|
- Wiki, notes, revisions and note-database workflows;
|
||||||
|
- discussions, channels, topics, messages and attachments;
|
||||||
|
- documents, templates, PDFs, revisions and public-link workflows;
|
||||||
|
- organizations, members, invitations, ACL and two-factor policies;
|
||||||
|
- notifications, preferences and read-state workflows;
|
||||||
|
- inventory, assets, stock, procurement and stocktake workflows;
|
||||||
|
- agent/controller diagnostics, tunnels, endpoints and runtime actions;
|
||||||
|
- status-page monitors, groups, history and incidents;
|
||||||
|
- realtime sessions, event cursors and operational activity;
|
||||||
|
- Terms documents, revisions, completeness and contract discussions;
|
||||||
|
- linked Gitea issues, labels, comments and attachments;
|
||||||
|
- safe bulk-change planning and validation rules.
|
||||||
|
|
||||||
|
When the connected MCP host supports MCP Apps UI, the 44 SHD render tools have
|
||||||
|
inline interactive widgets: active projects, module registers and specialized
|
||||||
|
Kanban, workload, timeline, funnel, dashboard, calendar, matrix, tree and
|
||||||
|
Project DB views. Each widget sorts or filters normalized records, shows
|
||||||
|
status/value fields, can load one selected record’s details where supported and
|
||||||
|
exposes only the write actions declared by its render metadata. A write always
|
||||||
|
requires an explicit form submission; destructive actions also require a
|
||||||
|
confirmation checkbox. After a successful mutation, the widget refreshes its
|
||||||
|
source list.
|
||||||
|
The same tools still return normal structured data for hosts that do not render
|
||||||
|
widgets.
|
||||||
|
|
||||||
|
The controller observability render tools share the versioned
|
||||||
|
`widgets/controller-observability/v1/` resource. It exposes controller device
|
||||||
|
inventory and profiles, normalized live states, server-computed binary/numeric
|
||||||
|
statistics, explicit native vs Influx/Grafana provenance, universal comparison
|
||||||
|
presets, complete controller topology, telemetry health and operation audit.
|
||||||
|
Expert-setting controls are enabled only for source-provided writable descriptors.
|
||||||
|
|
||||||
|
The active-projects source is `widgets/active-projects/source.html` with its
|
||||||
|
resource metadata in `widgets/active-projects/widget.json`. Before starting
|
||||||
|
the backend, generate the ignored runtime files with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node scripts/generate-widget-artifacts.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
The backend then reads the generated `widgets/active-projects/v1/` artifact for
|
||||||
|
the `ui://shd/active-projects/v1.html` resource.
|
||||||
|
|
||||||
|
The standard module resources are generated from the single
|
||||||
|
`widgets/_shared/universal-widget.html` source by
|
||||||
|
`scripts/generate-widget-artifacts.mjs`. Each generated artifact still keeps
|
||||||
|
its own URI, manifest, SHA256 checksum and provenance note: this preserves
|
||||||
|
resource cache keys and module-scope registration while removing duplicated
|
||||||
|
HTML, bridge and action runtime code. The generator also injects the shared
|
||||||
|
`widgets/_shared/widget-theme.css` and `widgets/_shared/write-actions.css`
|
||||||
|
into the specialized `active-projects` and `controller-observability`
|
||||||
|
renderers, so every widget uses the same spacing, surfaces, controls, tables
|
||||||
|
and responsive rules without changing their data contracts. The generator
|
||||||
|
supports `--check` for a no-write consistency check. Module copy and fallback
|
||||||
|
tool configuration lives in the sibling
|
||||||
|
`widgets/_shared/universal-widget-config.json` source and is injected into
|
||||||
|
each universal standalone artifact by the generator. For an older template
|
||||||
|
that still contains the inline configuration block, run `--extract-config`
|
||||||
|
once and then `--migrate-template` once. The specialized applications remain
|
||||||
|
separate only where their interaction contracts differ. Render tools are
|
||||||
|
deliberately separate from list tools so data access and UI rendering remain
|
||||||
|
auditable.
|
||||||
|
|
||||||
|
The generated `widgets/*/v1/` directories are ignored and must not be
|
||||||
|
committed. A fresh checkout or deployment must run the generator before the
|
||||||
|
MCP backend starts; `--check` can then verify that the local runtime output is
|
||||||
|
current.
|
||||||
|
|
||||||
|
## MCP connection
|
||||||
|
|
||||||
|
The package points to `https://shd.xyz.su/mcp` and requests OAuth for that MCP
|
||||||
|
resource. The client stores the resulting authorization; this repository never
|
||||||
|
contains an access token.
|
||||||
|
|
||||||
|
If a client or SHD installation uses another official endpoint, change the
|
||||||
|
connection in `.mcp.json` in that client's secure configuration. Do not commit
|
||||||
|
tokens, cookies, client secrets or private host credentials.
|
||||||
Binary file not shown.
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: shd-active-projects
|
||||||
|
description: Use when the user asks for all active SHD projects, project deadlines, project statuses, or a project register sorted by the nearest completion date.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Active SHD projects
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Call `shd_list_projects` once with `archived: false` and `limit: 100`.
|
||||||
|
2. If the user supplied a search term, pass it as `q`; otherwise do not add a
|
||||||
|
search filter.
|
||||||
|
3. Use the project fields returned by that tool. Do not call
|
||||||
|
`shd_get_project` once per row just to repeat list data.
|
||||||
|
4. Sort projects with a non-null `deadline` in ascending order. Put projects
|
||||||
|
without a deadline after dated projects; do not invent a date.
|
||||||
|
5. If the user is in a UI-capable MCP host, call `shd_render_projects_widget`
|
||||||
|
after the data call, passing the returned `data` array as `projects` and the
|
||||||
|
returned `meta` object as `meta`. When rendering succeeds, keep the project
|
||||||
|
rows inside the widget and give only a short confirmation in the final
|
||||||
|
response; do not repeat the rows as prose or a Markdown table.
|
||||||
|
6. If the UI host is unavailable or the render tool returns an error, return
|
||||||
|
the compact fallback table:
|
||||||
|
|
||||||
|
`Проект | Дата завершения | Статус`
|
||||||
|
|
||||||
|
7. Preserve the server's project name/code and status. If a deadline is a
|
||||||
|
datetime, format it with its returned timezone when available; otherwise
|
||||||
|
keep the value unmodified and state that timezone data was unavailable.
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- “Активные” defaults to non-archived projects (`archived: false`). If the user
|
||||||
|
means a particular business status rather than archive state, ask which
|
||||||
|
status or report the returned statuses without filtering them locally.
|
||||||
|
- Do not use task due dates as project completion dates.
|
||||||
|
- If the server indicates more records than the tool can return, report that
|
||||||
|
the current tool limit was reached; do not claim a complete list.
|
||||||
|
- This workflow is read-only and must not update, archive or delete projects.
|
||||||
|
|
||||||
|
See `references/project-widget.md` for the data/render boundary.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: Active SHD projects
|
||||||
|
short_description: Project deadlines and statuses
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Project widget contract
|
||||||
|
|
||||||
|
Call the data tool first. Pass only the returned project array and metadata to
|
||||||
|
the render tool. The widget is a presentation layer: it must not invent dates,
|
||||||
|
statuses or project rows. When the render succeeds, keep the rows in the
|
||||||
|
widget and do not repeat them in the assistant response. Keep the plain
|
||||||
|
structured table only as the fallback for hosts without MCP Apps UI or when
|
||||||
|
the render tool fails.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
name: shd-agents-operations
|
||||||
|
description: Use when the user asks to inspect SHD agents, controllers, diagnostics, tunnels, access rules, endpoints, monitoring or agent runtime actions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD agents operations
|
||||||
|
|
||||||
|
See `references/agent-operations.md` before touching runtime, network or access
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
## Diagnose first
|
||||||
|
|
||||||
|
- Resolve the controller, user agent or tunnel identity from
|
||||||
|
`shd_agents_overview`, status and diagnostic tools.
|
||||||
|
- Read current access policies, settings, history and async status before
|
||||||
|
proposing an action.
|
||||||
|
- Keep diagnostics, configuration, endpoint publication and network routing as
|
||||||
|
separate operations. Never expose credentials, private keys or tunnel data.
|
||||||
|
|
||||||
|
## Controller data and widgets
|
||||||
|
|
||||||
|
- For a controller device list, call `shd_get_controller_source_inventory`, then
|
||||||
|
`shd_render_controller_devices_widget` with the complete result and scoped
|
||||||
|
`meta.sourceArgs`.
|
||||||
|
- For current states across controllers, call `shd_get_home_live_state`, then
|
||||||
|
`shd_render_controller_live_state_widget`.
|
||||||
|
- For one presence, binary state or numeric sensor statistics, call
|
||||||
|
`shd_get_controller_state_statistics`, then
|
||||||
|
`shd_render_controller_statistics_widget`. The server result is authoritative
|
||||||
|
for counts, intervals, durations, percentages, medians and hourly buckets;
|
||||||
|
do not calculate them from raw history in the model.
|
||||||
|
- For raw time-series history, call `shd_get_controller_source_history`, then
|
||||||
|
`shd_render_controller_history_widget`. Keep the detailed rows in the widget
|
||||||
|
after a successful render and use plain text only when the UI call is
|
||||||
|
unavailable or fails.
|
||||||
|
|
||||||
|
## High-impact actions
|
||||||
|
|
||||||
|
Creating/closing tunnels, publishing or revoking endpoints, replacing access
|
||||||
|
policies, rebooting an agent, changing exit-network routing or enabling
|
||||||
|
monitoring requires an explicit target and user request. For asynchronous
|
||||||
|
actions, return the operation ID, poll only as needed and report terminal
|
||||||
|
status. Do not retry a network or reboot action blindly.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD agents operations"
|
||||||
|
short_description: "Diagnose agents and guarded runtime actions"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Agent operations contract
|
||||||
|
|
||||||
|
Start with `shd_agents_overview`, `shd_get_controller_status`,
|
||||||
|
`shd_agents_get_controller_diagnostics`, `shd_agents_get_diagnostics`, history
|
||||||
|
and the relevant access-policy tools. Use `shd_agents_get_async_status` for an
|
||||||
|
operation already returned by the server.
|
||||||
|
|
||||||
|
Treat tunnels, endpoint publication/revocation, access-policy replacement,
|
||||||
|
agent reboot, exit-node/network routing and controller/user-agent settings as
|
||||||
|
high-impact mutations. Tools such as `shd_agents_create_tunnel`,
|
||||||
|
`shd_agents_close_tunnel`, `shd_agents_publish_controller_endpoint`,
|
||||||
|
`shd_agents_revoke_controller_endpoint`, `shd_agents_reboot_user_agent` and
|
||||||
|
`shd_agents_force_exit_network_route` require explicit authorization and exact
|
||||||
|
scope. Preserve audit/readback and never print secrets returned by diagnostics.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: shd-airbase-audit
|
||||||
|
description: Use when the user asks to inspect AirBase tables, validate project data, find duplicate or broken links, or audit AirBase quality without changing records.
|
||||||
|
---
|
||||||
|
|
||||||
|
# AirBase audit
|
||||||
|
|
||||||
|
See `references/audit-contract.md` for the bounded read and evidence format.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Resolve the canonical Base identity. A supplied `project_code` is accepted
|
||||||
|
as the legacy alias only when the server accepts it; prefer the returned
|
||||||
|
Base identity for subsequent calls.
|
||||||
|
2. If no table is named, call `shd_airbase_tables` first and select only the
|
||||||
|
relevant tables. Do not load every table by default.
|
||||||
|
3. Call `shd_airbase_schema` for each selected table before reading its
|
||||||
|
records. Pass the table explicitly when a single-table audit is requested.
|
||||||
|
4. Call `shd_airbase_records` with bounded reads and the selected table.
|
||||||
|
5. Compare records with the returned schema and report only evidence visible in
|
||||||
|
the response.
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
Check for duplicate business numbers, missing required values, invalid select
|
||||||
|
values, unresolved linked records, orphaned references, conflicting DALI or
|
||||||
|
electrical identifiers, formula/derived-field risks and incomplete source data
|
||||||
|
when the selected tables expose those fields.
|
||||||
|
|
||||||
|
Report `table → record → field → observed value → reason → priority`. Distinguish
|
||||||
|
confirmed defects from data that could not be checked because the schema or
|
||||||
|
records were unavailable.
|
||||||
|
|
||||||
|
## Safety
|
||||||
|
|
||||||
|
This workflow is read-only. Do not call create, update, delete, import, bulk
|
||||||
|
upsert or generic action tools while auditing. If the user asks to repair
|
||||||
|
findings, first present the proposed records and then use the dedicated
|
||||||
|
validation/dry-run contract before any explicit write.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: AirBase audit
|
||||||
|
short_description: Read-only schema and data audit
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# AirBase audit contract
|
||||||
|
|
||||||
|
Use this contract for every read-only AirBase audit.
|
||||||
|
|
||||||
|
1. Resolve the canonical Base identity before selecting tables. Treat a
|
||||||
|
user-provided project code as an alias until the server returns the
|
||||||
|
authoritative identity.
|
||||||
|
2. Discover tables only when the request does not name them. Do not load every
|
||||||
|
table just to find a possible issue.
|
||||||
|
3. Read the schema before records for every selected table. Keep the table
|
||||||
|
name explicit in each subsequent request.
|
||||||
|
4. Bound record reads with the smallest useful limit and preserve the server's
|
||||||
|
pagination or truncation metadata.
|
||||||
|
5. Report evidence as `table → record → field → observed value → reason →
|
||||||
|
priority`. Mark a finding as unverified when the schema or record needed to
|
||||||
|
prove it was unavailable.
|
||||||
|
|
||||||
|
Never repair an audit finding inside the audit workflow. A repair requires a
|
||||||
|
separate proposal, an explicit mutation request, the current schema and
|
||||||
|
records, the server's validation/dry-run contract, and final readback.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: shd-analytics
|
||||||
|
description: Use when the user asks for SHD project or company analytics, trends, comparisons, metrics, charts, reports, or time summaries.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD analytics and reports
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
1. Resolve the organization or project scope before aggregating values. Use an
|
||||||
|
exact `project_code` when the user supplied one; otherwise resolve it with
|
||||||
|
`shd_list_projects`.
|
||||||
|
2. Use `shd_get_project_context` for a bounded cross-module summary and request
|
||||||
|
only the sections needed for the question.
|
||||||
|
3. For task effort use `shd_get_task_time_report`; use
|
||||||
|
`shd_export_task_time_report` only when the user asks for an export.
|
||||||
|
4. For proposal analytics use `shd_get_proposal_public_analytics` only for a
|
||||||
|
resolved proposal or public analytics scope accepted by its schema.
|
||||||
|
5. If the requested metric does not map to a known tool, call
|
||||||
|
`shd_capabilities` and report the unavailable capability instead of inventing
|
||||||
|
an endpoint or field.
|
||||||
|
|
||||||
|
## Reporting rules
|
||||||
|
|
||||||
|
- State the period, scope, currency and timezone returned by SHD.
|
||||||
|
- Distinguish totals returned by the server from calculations made from rows.
|
||||||
|
- Preserve null, unavailable and permission-denied sections as separate states.
|
||||||
|
- Do not infer profitability, completion, approval or trend direction from one
|
||||||
|
record or from a missing section.
|
||||||
|
- Present chart-ready data as a compact table with labels, units and source
|
||||||
|
fields. Use a UI resource only when the server advertises a matching widget.
|
||||||
|
|
||||||
|
See `references/analytics-contract.md` before composing a multi-section report.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD analytics
|
||||||
|
short_description: Metrics, comparisons and reports
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Analytics contract
|
||||||
|
|
||||||
|
- Every metric has an explicit scope, period, unit and source operation.
|
||||||
|
- A server total is authoritative; a locally calculated total must be marked as
|
||||||
|
calculated and list the rows used.
|
||||||
|
- A comparison must use the same period boundaries and unit on both sides.
|
||||||
|
- Missing data, denied access and a zero value are different outcomes.
|
||||||
|
- Recommendations follow the evidence and are not written back to SHD unless a
|
||||||
|
separate mutation is explicitly requested.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: shd-discussions
|
||||||
|
description: Use when the user asks to find, read, search, create or edit SHD discussion channels, topics, messages or attachments.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD discussions
|
||||||
|
|
||||||
|
See `references/discussion-contract.md` for the tool map and message safety
|
||||||
|
rules.
|
||||||
|
|
||||||
|
## Resolve and read
|
||||||
|
|
||||||
|
- Resolve the organization or project context before selecting a channel.
|
||||||
|
- Use `shd_list_discussion_channels` and `shd_list_discussion_topics` before
|
||||||
|
reading messages when an ID is not already known.
|
||||||
|
- Use bounded reads with `shd_list_discussion_messages` or
|
||||||
|
`shd_search_discussions`; preserve pagination and returned identifiers.
|
||||||
|
- Treat private channels and messages as server-authorized data. Do not infer
|
||||||
|
membership or quote content outside the returned scope.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
Only create or edit a message, or upload an attachment, when the user asks for
|
||||||
|
that exact operation. Confirm the target topic, preserve the latest message
|
||||||
|
identity/version when available, and report the server result. Never delete or
|
||||||
|
rewrite discussion history by guessing a topic from its display name.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD discussions"
|
||||||
|
short_description: "Search and manage discussion threads"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Discussion contract
|
||||||
|
|
||||||
|
Use the narrowest tool for the request:
|
||||||
|
|
||||||
|
- discovery: `shd_list_discussion_channels`,
|
||||||
|
`shd_list_discussion_topics`;
|
||||||
|
- content: `shd_list_discussion_messages`, `shd_search_discussions`;
|
||||||
|
- writes: `shd_add_discussion_message`, `shd_update_discussion_message`;
|
||||||
|
- files: `shd_upload_discussion_attachment`.
|
||||||
|
|
||||||
|
Resolve the canonical channel/topic/message identifiers before a write. Keep
|
||||||
|
searches bounded, distinguish no results from denied scope, and retain the
|
||||||
|
server's pagination or conflict fields. A message edit or attachment upload is
|
||||||
|
a side effect: require explicit user intent and report the final server result.
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: shd-documents
|
||||||
|
description: Use when the user asks to inspect, generate, send, publish, revise or audit SHD documents, templates, comments or public links.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD documents
|
||||||
|
|
||||||
|
See `references/document-safety.md` before handling publication, PDF or
|
||||||
|
contract-related document actions.
|
||||||
|
|
||||||
|
## Read and prepare
|
||||||
|
|
||||||
|
- Resolve the project and document identifiers before reading content.
|
||||||
|
- Read the document, relevant template/configuration, revisions and comments
|
||||||
|
needed for the requested result; do not treat a filename as authorization.
|
||||||
|
- Keep document status, approval state, external links and public links
|
||||||
|
separate in the report.
|
||||||
|
- Use the document's returned revision or version when a write accepts one.
|
||||||
|
- In a UI-capable MCP host, pass normalized list records to
|
||||||
|
`shd_render_documents_widget`; keep the structured document list as the
|
||||||
|
fallback and use `shd_get_contract` only for an explicitly selected record.
|
||||||
|
|
||||||
|
## Mutations and publication
|
||||||
|
|
||||||
|
Generating a PDF, sending a document, saving a contract, publishing a public
|
||||||
|
link or changing document state requires an explicit request. Explain the
|
||||||
|
target and audience before publication, preserve the server's ACL and expiry
|
||||||
|
rules, and perform readback when the tool provides it. Never expose private
|
||||||
|
links, bearer values or document content outside the authorized result.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD documents"
|
||||||
|
short_description: "Inspect and safely manage documents"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Document safety contract
|
||||||
|
|
||||||
|
Useful read tools include `shd_list_contracts`, `shd_get_contract`,
|
||||||
|
`shd_list_document_revisions`, `shd_list_document_templates`,
|
||||||
|
`shd_list_document_comments`, `shd_list_document_external_links` and
|
||||||
|
`shd_list_document_public_links`. PDF inspection uses
|
||||||
|
`shd_download_document_pdf`; generation uses `shd_generate_document_pdf`.
|
||||||
|
|
||||||
|
Treat these as separate side effects: `shd_send_document`,
|
||||||
|
`shd_publish_document_public_link`, `shd_save_document_contract` and
|
||||||
|
`shd_document_contract_workflow`. Before any of them, resolve the document,
|
||||||
|
read the current state and require the exact requested action. Report recipient,
|
||||||
|
visibility, expiry, revision and server validation when returned. A public link
|
||||||
|
is never a harmless preview and must not be created just to test access.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: shd-entity-resolution
|
||||||
|
description: Use when the user asks to find a CRM entity, resolve duplicate names, compare counterparties, or determine whether records refer to the same business object.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD entity resolution
|
||||||
|
|
||||||
|
## Resolve first
|
||||||
|
|
||||||
|
1. Prefer an exact returned ID, code or external reference over a name.
|
||||||
|
2. Search with `shd_list_crm`, then read candidates with
|
||||||
|
`shd_get_crm_entity`.
|
||||||
|
3. Inspect `shd_list_crm_entity_links` when relationships, projects or deals
|
||||||
|
are part of the identity decision.
|
||||||
|
4. Compare only fields returned by SHD. Report candidate records, matching
|
||||||
|
evidence, conflicting evidence and unresolved fields separately.
|
||||||
|
|
||||||
|
## Safety
|
||||||
|
|
||||||
|
- A similar name is not proof of a duplicate.
|
||||||
|
- Do not merge, delete, reassign ownership or change a relationship while
|
||||||
|
resolving identity.
|
||||||
|
- A write requires an exact target, an explicit user request and the current
|
||||||
|
server validation contract. Read the record again after a permitted write.
|
||||||
|
|
||||||
|
See `references/entity-resolution.md` for the candidate-report format.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD entity resolution
|
||||||
|
short_description: Resolve CRM records and duplicates
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Entity-resolution report
|
||||||
|
|
||||||
|
Report each candidate as:
|
||||||
|
|
||||||
|
`candidate → identifiers → matching fields → conflicting fields → related records → confidence → next action`
|
||||||
|
|
||||||
|
Confidence is an explanation of returned evidence, not a server permission or
|
||||||
|
an authorization to merge records.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: shd-estimate-management
|
||||||
|
description: Use when the user asks to inspect, compare, validate, create or update SHD estimates, proposals, terms or document contracts.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD estimates and proposal documents
|
||||||
|
|
||||||
|
## Read path
|
||||||
|
|
||||||
|
1. Resolve the project, proposal, terms document or contract before acting.
|
||||||
|
2. Use `shd_list_proposals` and `shd_get_proposal` for proposal registers and
|
||||||
|
details.
|
||||||
|
3. Use `shd_list_contracts`, `shd_get_contract`,
|
||||||
|
`shd_get_pdf_scheme_editor_completeness` and `shd_validate_pdf_scheme_editor_document` for
|
||||||
|
terms and completeness checks.
|
||||||
|
4. Inspect template capabilities before proposing a template-driven change.
|
||||||
|
5. Report line items, versions, completeness, approvals and publication state
|
||||||
|
only when those fields are returned by the server.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
- Creating, replacing items, requesting approval, publishing, restoring or
|
||||||
|
deleting a proposal/document requires an exact explicit request.
|
||||||
|
- Read the latest version and use the tool's concurrency and idempotency fields
|
||||||
|
before a write.
|
||||||
|
- Never publish or request approval as a side effect of an audit or comparison.
|
||||||
|
|
||||||
|
See `references/estimate-safety.md` before a proposal or terms mutation.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD estimates and proposals
|
||||||
|
short_description: Estimate and document workflows
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Estimate and proposal safety
|
||||||
|
|
||||||
|
- Draft, validated, approved and published are separate states.
|
||||||
|
- A calculated total is not an approval and a generated PDF is not a
|
||||||
|
publication.
|
||||||
|
- Compare revisions before restoring or replacing a document.
|
||||||
|
- Keep the project, counterparty and version identifiers in the final report.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: shd-event-positions
|
||||||
|
description: Use when the user asks about SHD events, booking availability, scheduling slots, event types or booking positions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD events and scheduling
|
||||||
|
|
||||||
|
## Read path
|
||||||
|
|
||||||
|
1. Start with `shd_get_scheduling_overview` when the scope is broad.
|
||||||
|
2. Use `shd_list_scheduling_event_types`,
|
||||||
|
`shd_list_scheduling_availability`, `shd_list_scheduling_slots` and
|
||||||
|
`shd_list_scheduling_bookings` for the exact requested view.
|
||||||
|
3. Preserve the returned timezone, slot state, booking state and integration
|
||||||
|
status. Do not turn an available slot into a confirmed booking.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
`shd_hold_scheduling_slot`, `shd_create_scheduling_booking`,
|
||||||
|
`shd_confirm_scheduling_booking`, `shd_reschedule_scheduling_booking` and
|
||||||
|
`shd_cancel_scheduling_booking` are explicit writes. Read the current slot or
|
||||||
|
booking first, pass the current concurrency/idempotency values accepted by the
|
||||||
|
schema and report the final server state.
|
||||||
|
|
||||||
|
See `references/scheduling-safety.md` for the state boundaries.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD events and scheduling
|
||||||
|
short_description: Slots, bookings and event positions
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Scheduling state boundaries
|
||||||
|
|
||||||
|
- Availability is not a hold.
|
||||||
|
- A hold is not a booking.
|
||||||
|
- A booking is not confirmed until SHD returns confirmation.
|
||||||
|
- A cancellation or reschedule must identify the exact booking and preserve
|
||||||
|
the server's conflict response.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: shd-finance-crm
|
||||||
|
description: Use when the user asks for finance or CRM information, document/payment checks, customer records, deals, relationships, or a project business overview in SHD.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD Finance and CRM
|
||||||
|
|
||||||
|
For a high-level project overview, prefer `shd_get_project_context` and request
|
||||||
|
only the `finance` or `summary` sections needed. For a detailed request use
|
||||||
|
the module tools below.
|
||||||
|
|
||||||
|
## Finance
|
||||||
|
|
||||||
|
- Find documents with `shd_list_finance_documents`.
|
||||||
|
- Read one with `shd_get_finance_document`.
|
||||||
|
- Read payments with `shd_list_finance_payments` or
|
||||||
|
`shd_get_finance_payment`.
|
||||||
|
- Resolve allowed references with `shd_list_finance_refs`.
|
||||||
|
- In a UI-capable MCP host, pass normalized finance records to
|
||||||
|
`shd_render_finance_widget`; keep the structured list as the fallback.
|
||||||
|
|
||||||
|
When auditing, compare document type, project/counterparty links, line items,
|
||||||
|
currency, totals, due dates, payment state and returned validation. Never infer
|
||||||
|
approval, payment or publication from a document's title or amount.
|
||||||
|
|
||||||
|
## CRM
|
||||||
|
|
||||||
|
- Find entities with `shd_list_crm` and read one with `shd_get_crm_entity`.
|
||||||
|
- In a UI-capable MCP host, pass normalized CRM records to
|
||||||
|
`shd_render_crm_widget`; keep the structured list as the fallback.
|
||||||
|
- Resolve relationships with `shd_list_crm_entity_links` and inspect available
|
||||||
|
pipelines/stages before a deal change.
|
||||||
|
- Use `shd_save_crm_entity` only for an explicitly requested create/update after
|
||||||
|
duplicate and relationship checks.
|
||||||
|
|
||||||
|
Do not approve, publish, delete, merge, move a deal stage, create a payment or
|
||||||
|
change ownership unless the user explicitly requests that exact operation.
|
||||||
|
Report IDs, resolved links, changed fields and server validation separately from
|
||||||
|
recommendations.
|
||||||
|
|
||||||
|
See `references/finance-crm-audit.md` for the finance/CRM audit boundary.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD finance and CRM
|
||||||
|
short_description: Finance and customer workflows
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Finance and CRM audit boundary
|
||||||
|
|
||||||
|
Finance documents, payments, allocations, CRM entities, links and stages are
|
||||||
|
different objects. Report their identifiers and returned state separately.
|
||||||
|
Use the dedicated `shd-financial-account-audit` workflow for reconciliation,
|
||||||
|
balances, settlements or audit-log questions.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: shd-financial-account-audit
|
||||||
|
description: Use when the user asks to audit SHD finance accounts, balances, payments, documents, allocations, settlements or finance history.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD financial audit
|
||||||
|
|
||||||
|
## Read path
|
||||||
|
|
||||||
|
1. Resolve project, legal entity, counterparty and period before reading.
|
||||||
|
2. Use `shd_list_finance_refs` for allowed categories, accounts and reference
|
||||||
|
values when the request depends on them.
|
||||||
|
3. Read documents and payments with `shd_list_finance_documents`,
|
||||||
|
`shd_get_finance_document`, `shd_list_finance_payments` and
|
||||||
|
`shd_get_finance_payment`.
|
||||||
|
4. Use `shd_get_finance_account_balance` and
|
||||||
|
`shd_list_finance_audit_log` when balances or history are requested.
|
||||||
|
5. Reconcile only records returned for the same scope and period. Mark missing
|
||||||
|
links, currency differences and permission gaps explicitly.
|
||||||
|
|
||||||
|
## Settlement safety
|
||||||
|
|
||||||
|
- Allocation, removal of allocation, payment creation, approval, deletion and
|
||||||
|
document replacement are writes; do not perform them during an audit.
|
||||||
|
- A document total is not proof of payment, and a payment is not proof of
|
||||||
|
allocation.
|
||||||
|
- For an explicitly requested write, read the latest payment/document and use
|
||||||
|
the server's idempotency and concurrency contract, then read back the result.
|
||||||
|
|
||||||
|
See `references/finance-audit.md` for the reconciliation format.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD financial audit
|
||||||
|
short_description: Balances, payments and settlements
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Finance audit format
|
||||||
|
|
||||||
|
Report:
|
||||||
|
|
||||||
|
`scope → period → currency → source record → observed amount/state → linked record → discrepancy → priority`
|
||||||
|
|
||||||
|
Do not hide an unresolved counterparty, currency conversion, allocation or
|
||||||
|
permission boundary behind an aggregate number.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: shd-gitea
|
||||||
|
description: Use when the user asks to inspect or manage SHD-linked Gitea issues, labels, comments or issue attachments.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD Gitea
|
||||||
|
|
||||||
|
See `references/gitea-contract.md` for repository and issue safety.
|
||||||
|
|
||||||
|
## Read and resolve
|
||||||
|
|
||||||
|
- Resolve the canonical repository and issue identifiers returned by SHD before
|
||||||
|
acting.
|
||||||
|
- Read the issue, labels and current state before creating a comment or
|
||||||
|
changing labels.
|
||||||
|
- Keep Gitea state separate from SHD project/task state; do not claim that a
|
||||||
|
label or issue update changed the SHD record unless the server says so.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
Creating an issue, commenting, changing labels or uploading an attachment
|
||||||
|
requires an explicit request. Report repository, issue number, changed labels,
|
||||||
|
comment/attachment result and server validation. Never put credentials or
|
||||||
|
private attachment URLs in the response or repository.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD Gitea"
|
||||||
|
short_description: "Review and update linked Gitea issues"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Gitea contract
|
||||||
|
|
||||||
|
Use `shd_list_gitea_issues` and `shd_list_gitea_labels` for discovery. Writes
|
||||||
|
are `shd_create_gitea_issue`, `shd_comment_gitea_issue`,
|
||||||
|
`shd_update_gitea_issue_labels` and `shd_upload_gitea_issue_attachment`.
|
||||||
|
|
||||||
|
Resolve the repository and issue from returned server data, preserve the
|
||||||
|
current issue state before a label update, and use an idempotency key when the
|
||||||
|
tool schema offers one. A comment or attachment is an external side effect;
|
||||||
|
require exact user intent and report readback. Do not assume Gitea permissions
|
||||||
|
from SHD project permissions alone.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: shd-inventory
|
||||||
|
description: Use when the user asks to inspect or change SHD inventory items, assets, stock, procurement, reservations, receipts or stocktakes.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD inventory
|
||||||
|
|
||||||
|
See `references/inventory-safety.md` before any stock, asset or procurement
|
||||||
|
mutation.
|
||||||
|
|
||||||
|
## Inspect
|
||||||
|
|
||||||
|
- Resolve the project, location, item, asset or procurement identity from
|
||||||
|
server results.
|
||||||
|
- Read current stock, lots, reservations, movements, receipts, maintenance,
|
||||||
|
assignments and audit data relevant to the question.
|
||||||
|
- Keep available, reserved, received, written-off and counted quantities
|
||||||
|
distinct. Do not calculate an authoritative balance from partial pages.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
Reserve, release, receive, write off or close a stocktake only on an explicit
|
||||||
|
request. Creating/updating resources and generic inventory actions require the
|
||||||
|
same read-before-write and conflict/idempotency handling. Report quantities,
|
||||||
|
location, source document, server validation and readback; never silently
|
||||||
|
repair a discrepancy by changing stock.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD inventory"
|
||||||
|
short_description: "Audit stock, assets and procurement"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Inventory safety contract
|
||||||
|
|
||||||
|
Read tools cover items/assets, procurement, locations, movements, lots,
|
||||||
|
reservations, receipts, stocktakes, labels, assignments, maintenance and the
|
||||||
|
inventory audit log. Use the narrowest `shd_list_inventory_*` or
|
||||||
|
`shd_get_inventory_*` tool and preserve pagination.
|
||||||
|
|
||||||
|
Side-effecting operations include `shd_reserve_inventory_stock`,
|
||||||
|
`shd_release_inventory_stock`, `shd_receive_inventory_stock`,
|
||||||
|
`shd_write_off_inventory_stock`, `shd_close_inventory_stocktake`,
|
||||||
|
`shd_create_inventory_resource`, `shd_update_inventory_resource` and
|
||||||
|
`shd_inventory_action`. Resolve the exact target and latest quantity first;
|
||||||
|
require explicit confirmation for irreversible or bulk operations and verify
|
||||||
|
the resulting movement/audit record.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: shd-notifications
|
||||||
|
description: Use when the user asks to inspect SHD notifications, notification modules, read state or preferences, or mark notifications read.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD notifications
|
||||||
|
|
||||||
|
See `references/notification-contract.md` for read-state and preference
|
||||||
|
boundaries.
|
||||||
|
|
||||||
|
## Read
|
||||||
|
|
||||||
|
- Use `shd_list_notifications` with bounded filters and preserve notification
|
||||||
|
IDs and timestamps.
|
||||||
|
- Use module, read-state and preference tools only for the authenticated user
|
||||||
|
or the explicitly authorized scope.
|
||||||
|
- Distinguish unread, read, muted and unavailable data; do not invent a missing
|
||||||
|
notification source.
|
||||||
|
|
||||||
|
## State changes
|
||||||
|
|
||||||
|
Marking one or more notifications read or changing preferences changes user
|
||||||
|
state. Do it only when explicitly requested, use the returned IDs, and report
|
||||||
|
the number and final state. Never mark a whole feed read merely because it was
|
||||||
|
displayed or summarized.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD notifications"
|
||||||
|
short_description: "Review alerts and notification state"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Notification contract
|
||||||
|
|
||||||
|
Read with `shd_list_notifications`, `shd_list_notification_modules`,
|
||||||
|
`shd_get_notification_read_state`, `shd_get_notification_preferences` and,
|
||||||
|
for document-specific items, `shd_list_document_notifications`.
|
||||||
|
|
||||||
|
State-changing tools are `shd_update_notification_preferences`,
|
||||||
|
`shd_mark_notification_read`, `shd_mark_all_notifications_read` and
|
||||||
|
`shd_mark_notifications_read`. Require explicit scope for bulk marking,
|
||||||
|
prefer stable notification IDs, and report server readback. A list response is
|
||||||
|
not authorization to alter every item in it.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: shd-organizations-acl
|
||||||
|
description: Use when the user asks to inspect or change SHD organizations, members, invitations, roles, access audits or two-factor policies.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD organizations and ACL
|
||||||
|
|
||||||
|
See `references/organization-acl.md` for identity resolution and mutation
|
||||||
|
boundaries.
|
||||||
|
|
||||||
|
## Inspect
|
||||||
|
|
||||||
|
- Resolve the canonical organization ID; never use a guessed name as a write
|
||||||
|
target.
|
||||||
|
- Read members, invitations, audit entries and the current two-factor policy
|
||||||
|
before explaining access.
|
||||||
|
- Report organization, user, role, invitation state and policy as separate
|
||||||
|
facts. A denied or incomplete list is not proof that a member is absent.
|
||||||
|
|
||||||
|
## Change access
|
||||||
|
|
||||||
|
Inviting, adding, updating or removing a member, changing a two-factor policy,
|
||||||
|
or creating, archiving or restoring an organization requires explicit intent.
|
||||||
|
Show the exact organization and user scope before the mutation. Preserve server
|
||||||
|
authorization, conflict checks and audit readback; never broaden access to
|
||||||
|
make a failed request succeed.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD organizations and ACL"
|
||||||
|
short_description: "Audit members, roles and access policies"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Organization and ACL contract
|
||||||
|
|
||||||
|
Read with `shd_list_organization_members`, `shd_list_organization_invitations`,
|
||||||
|
`shd_list_organization_audit` and
|
||||||
|
`shd_get_organization_two_factor_policy`. Resolve users and organizations from
|
||||||
|
server-returned IDs.
|
||||||
|
|
||||||
|
Mutations include invitation operations, member add/update/remove,
|
||||||
|
organization create/update/archive/restore and
|
||||||
|
`shd_update_organization_two_factor_policy`. Every mutation needs explicit
|
||||||
|
authorization, the latest relevant state, and final server/audit readback when
|
||||||
|
available. Do not disclose invitation secrets or use an email/name match as a
|
||||||
|
substitute for a returned user identity.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: shd-project-files
|
||||||
|
description: Use when the user asks to inspect project files, obtain a file link, share or zip a file, rename or move a path, or delete project files.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD project files
|
||||||
|
|
||||||
|
## Resolve paths
|
||||||
|
|
||||||
|
1. Call `shd_list_files` with the exact `project_code` and, when supplied, the
|
||||||
|
path.
|
||||||
|
2. Resolve the exact file or directory path and distinguish files from
|
||||||
|
directories before any action.
|
||||||
|
3. Use `shd_get_file_link` only when the user asks for a temporary file link or
|
||||||
|
download.
|
||||||
|
|
||||||
|
## Actions
|
||||||
|
|
||||||
|
`shd_create_file_action` supports the explicit actions `share-link`, `zip-link`,
|
||||||
|
`rename`, `move` and `delete`. Use the exact action matching the request. For
|
||||||
|
rename, move and delete, confirm the source/destination and replacement scope
|
||||||
|
from the current listing first. Mutating actions require a stable
|
||||||
|
`idempotency_key`; delete additionally requires `confirm: true` and the server
|
||||||
|
version/confirmation fields accepted by its schema.
|
||||||
|
|
||||||
|
Do not expose bearer tokens or raw secret material in prose or logs. Return a
|
||||||
|
temporary URL only when the user explicitly asks for it. Do not overwrite,
|
||||||
|
publish, share or delete a path merely because the user asked to inspect it.
|
||||||
|
|
||||||
|
See `references/file-actions.md` for the file-action boundary.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD project files
|
||||||
|
short_description: Inspect and safely manage files
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# File action boundary
|
||||||
|
|
||||||
|
Resolve the exact project path first. Inspection, temporary link retrieval,
|
||||||
|
share/zip, rename/move and delete are separate intents. Mutating actions need
|
||||||
|
the server's current version, confirmation and idempotency fields.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: shd-project-status
|
||||||
|
description: Use when the user asks for a status report, operational summary, risks, blockers, next actions, or a cross-module overview of one SHD project.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD project status
|
||||||
|
|
||||||
|
## Resolve the project
|
||||||
|
|
||||||
|
- If the user supplied an exact project code, use it directly.
|
||||||
|
- If the user supplied only a name, call `shd_list_projects` with `q` once,
|
||||||
|
choose only an unambiguous exact match, and ask for the project code when
|
||||||
|
multiple projects match.
|
||||||
|
|
||||||
|
## Read the context
|
||||||
|
|
||||||
|
For a full summary call `shd_get_project_context` with the resolved
|
||||||
|
`project_code`, `include` set to the requested sections, and
|
||||||
|
`limit_per_section: 100`. The available sections are:
|
||||||
|
|
||||||
|
`summary`, `tasks`, `discussions`, `documents`, `finance`, `terms`, `agents`.
|
||||||
|
|
||||||
|
Request only the sections needed for a narrow question. Do not pretend that a
|
||||||
|
section is empty when the server reported it unavailable or the user lacks
|
||||||
|
access.
|
||||||
|
|
||||||
|
## Report
|
||||||
|
|
||||||
|
Separate the result into:
|
||||||
|
|
||||||
|
- confirmed project state;
|
||||||
|
- current deadlines or status values returned by SHD;
|
||||||
|
- blockers and risks supported by retrieved records;
|
||||||
|
- next actions, clearly marked as recommendations;
|
||||||
|
- unavailable or unresolved sections.
|
||||||
|
|
||||||
|
Do not convert a task deadline into a project deadline, infer a completion date
|
||||||
|
from activity, or claim deployment success from a status field. This workflow is
|
||||||
|
read-only unless the user separately requests a specific change.
|
||||||
|
|
||||||
|
See `references/context-sections.md` for section selection and unavailable data.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD project status
|
||||||
|
short_description: Project summaries and risks
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Project context sections
|
||||||
|
|
||||||
|
Use only the sections requested by the user. Available sections currently
|
||||||
|
include `summary`, `tasks`, `discussions`, `documents`, `finance`, `terms` and
|
||||||
|
`agents`; unavailable or denied sections must remain visible in the report.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
name: shd-realtime-activity
|
||||||
|
description: Use when the user asks to inspect SHD realtime events, start or terminate a realtime session, or review operational activity and audit history.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD realtime and activity
|
||||||
|
|
||||||
|
See `references/realtime-contract.md` for session lifecycle and event handling.
|
||||||
|
|
||||||
|
## Read and observe
|
||||||
|
|
||||||
|
- Resolve the authorized project/module scope before reading events or audit
|
||||||
|
history.
|
||||||
|
- Use bounded event reads and preserve event IDs, cursors and timestamps.
|
||||||
|
- Distinguish historical audit records from a live realtime session; do not
|
||||||
|
claim that an event stream is complete when it was truncated or disconnected.
|
||||||
|
|
||||||
|
## Session lifecycle
|
||||||
|
|
||||||
|
Starting or terminating a realtime session is a stateful operation. Require an
|
||||||
|
explicit request, retain the returned session ID and scope, avoid duplicate
|
||||||
|
starts on retry, and terminate only the requested session. Report connection
|
||||||
|
or terminal state rather than fabricating an event.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD realtime and activity"
|
||||||
|
short_description: "Inspect events and manage live sessions"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Realtime and activity contract
|
||||||
|
|
||||||
|
Use `shd_start_realtime_session`, `shd_get_realtime_events` and
|
||||||
|
`shd_terminate_realtime_session` for the session lifecycle. Use the relevant
|
||||||
|
read-only audit/activity tool for historical records, such as
|
||||||
|
`shd_get_audit_history`, when the request is about what already happened.
|
||||||
|
|
||||||
|
Keep session IDs, scopes, cursors and last-seen timestamps separate. A retry
|
||||||
|
must not create a second session unless the server explicitly supports an
|
||||||
|
idempotency key. Termination requires the exact returned session ID and should
|
||||||
|
be confirmed by terminal readback.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
name: shd-routing
|
||||||
|
description: Use for any request that asks SHD for data, a project operation, a module workflow, or a change to SHD records. Route the request to the narrowest existing SHD MCP tool and preserve server-side permissions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD routing and safety
|
||||||
|
|
||||||
|
SHD MCP is the source of truth for live data, tool schemas, permissions,
|
||||||
|
organization/project scope, conflicts, idempotency, audit and persisted
|
||||||
|
readback. Do not invent fields, statuses, identifiers, routes or capabilities.
|
||||||
|
|
||||||
|
## Route the request
|
||||||
|
|
||||||
|
- Project list, project status, deadlines or project context: use the Projects
|
||||||
|
Core tools and the `shd-active-projects` or `shd-project-status` workflow.
|
||||||
|
- AirBase schema, records, links or data quality: use AirBase tools and
|
||||||
|
the `shd-airbase-audit` workflow.
|
||||||
|
- Tasks, task projects, task status or task dates: use Tasks tools and the
|
||||||
|
`shd-task-workflow` workflow.
|
||||||
|
- Project files and paths: use Files tools and the `shd-project-files` workflow.
|
||||||
|
- Discussions, channels, topics, messages or attachments: use Discussions tools and the `shd-discussions` workflow.
|
||||||
|
- Documents, templates, revisions, PDFs or publication links: use Documents tools and the `shd-documents` workflow.
|
||||||
|
- Organizations, members, invitations, roles or two-factor policy: use Organizations tools and the `shd-organizations-acl` workflow.
|
||||||
|
- Notifications, preferences or read state: use Notifications tools and the `shd-notifications` workflow.
|
||||||
|
- Inventory, stock, assets, procurement or stocktakes: use Inventory tools and the `shd-inventory` workflow.
|
||||||
|
- Agents, controllers, diagnostics, tunnels or network access: use Agents tools and the `shd-agents-operations` workflow.
|
||||||
|
- Controller devices, current states, presence/history or sensor statistics: use the controller-integrations source tools; in a UI-capable host call the matching controller render tool after the data tool and before the final response (`shd_render_controller_devices_widget`, `shd_render_controller_live_state_widget`, `shd_render_controller_history_widget` or `shd_render_controller_statistics_widget`).
|
||||||
|
- Status-page monitors, history, groups or incidents: use Status Page tools and the `shd-status-page` workflow.
|
||||||
|
- Realtime sessions, event cursors or historical activity: use Realtime tools and the `shd-realtime-activity` workflow.
|
||||||
|
- Terms documents, revisions, completeness or contract discussions: use Terms tools and the `shd-terms-contracts` workflow.
|
||||||
|
- Linked Gitea issues, labels, comments or attachments: use Gitea tools and the `shd-gitea` workflow.
|
||||||
|
- Finance or CRM records: use the specific module tools and the
|
||||||
|
`shd-finance-crm` workflow; use project context for a high-level overview.
|
||||||
|
- Finance reconciliation, balances, settlements or audit history:
|
||||||
|
`shd-financial-account-audit`.
|
||||||
|
- Proposals, estimates, quotes or estimate-document contracts:
|
||||||
|
`shd-estimate-management`.
|
||||||
|
- Scheduling, event types, slots or bookings: `shd-event-positions`.
|
||||||
|
- Wiki, notes, pages, revisions or note databases: `shd-wiki-management`.
|
||||||
|
- Entity matching or possible duplicates: `shd-entity-resolution`.
|
||||||
|
- Analytics, comparisons, trends or reports: `shd-analytics`.
|
||||||
|
- A multi-record change: use `shd-safe-bulk-change` before any mutation.
|
||||||
|
|
||||||
|
Use `shd_capabilities` only when the available module or permission boundary is
|
||||||
|
unclear. If a tool or module is unavailable, report that fact instead of
|
||||||
|
falling back to an undocumented endpoint.
|
||||||
|
|
||||||
|
## Read-first rule
|
||||||
|
|
||||||
|
For every write, first read the target and its relevant schema or metadata.
|
||||||
|
Only perform a mutation when the user explicitly requested that mutation. A
|
||||||
|
request to inspect, summarize, compare, audit or suggest is not permission to
|
||||||
|
write.
|
||||||
|
|
||||||
|
Preserve the exact server result. Separate confirmed values from inference,
|
||||||
|
and include affected identifiers, conflicts, validation errors and final
|
||||||
|
readback when the server returns them.
|
||||||
|
|
||||||
|
## Scope and secrets
|
||||||
|
|
||||||
|
Never ask for or expose access tokens, passwords, cookies, private keys, raw
|
||||||
|
public-link bearer tokens or integration secrets. Never use a user-provided
|
||||||
|
project name as an identifier when SHD has not resolved it to a returned
|
||||||
|
project code or ID.
|
||||||
|
|
||||||
|
See `references/module-map.md` for the current high-level routing map.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD routing and safety
|
||||||
|
short_description: Route requests to safe MCP tools
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# SHD module map
|
||||||
|
|
||||||
|
- Projects and project context → `shd_list_projects`, `shd_get_project`,
|
||||||
|
`shd_get_project_context`.
|
||||||
|
- ProjectBase → `shd_project_db_*`, `shd_project_base_*`.
|
||||||
|
- Tasks → `shd_list_tasks`, `shd_get_task`, task mutation tools.
|
||||||
|
- Files → `shd_list_files`, file action tools.
|
||||||
|
- Discussions → channel, topic, message search and attachment tools.
|
||||||
|
- Documents → document, template, revision, PDF and publication-link tools.
|
||||||
|
- Organizations and ACL → members, invitations, audit and two-factor policy
|
||||||
|
tools.
|
||||||
|
- Notifications → notification, preference and read-state tools.
|
||||||
|
- Inventory → stock, asset, procurement, reservation and stocktake tools.
|
||||||
|
- Agents → controller/user-agent status, diagnostics, tunnels, endpoints and
|
||||||
|
network-operation tools.
|
||||||
|
- Status Page → monitor, history, group and incident tools.
|
||||||
|
- Realtime and activity → session, event-cursor and audit/activity tools.
|
||||||
|
- Terms and contracts → Terms documents, completeness, revisions and point
|
||||||
|
discussion tools.
|
||||||
|
- Gitea → linked issue, label, comment and attachment tools.
|
||||||
|
- Finance and CRM → module-specific finance/CRM tools.
|
||||||
|
- Proposals, terms and document contracts → proposal/terms/document tools.
|
||||||
|
- Scheduling → `shd_get_scheduling_overview` and scheduling tools.
|
||||||
|
- Wiki and notes → note-space, page, revision and database tools.
|
||||||
|
- Unknown or permission-sensitive capability → `shd_capabilities` first.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: shd-safe-bulk-change
|
||||||
|
description: Use before any SHD bulk create, update, import, archive, delete, or other multi-record mutation, and whenever the user asks to plan a mass change.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Safe SHD bulk changes
|
||||||
|
|
||||||
|
## Plan first
|
||||||
|
|
||||||
|
1. Confirm the target module, organization/project scope, exact match rule and
|
||||||
|
intended side effect.
|
||||||
|
2. Read current schemas and records. Estimate affected, skipped, invalid and
|
||||||
|
ambiguous rows.
|
||||||
|
3. Prefer a server validation or dry-run operation. For AirBase use
|
||||||
|
`shd_airbase_validate_records`, then
|
||||||
|
`shd_airbase_bulk_create_records` or
|
||||||
|
`shd_airbase_bulk_upsert_records` with `dry_run: true` when applicable.
|
||||||
|
4. Present the plan and stop unless the user explicitly authorized execution.
|
||||||
|
|
||||||
|
## Execute safely
|
||||||
|
|
||||||
|
Use stable idempotency keys, expected versions and exact match fields accepted
|
||||||
|
by the selected tool. Do not silently widen a project, organization, table or
|
||||||
|
record filter. Do not convert a failed or conflicting row into a new record.
|
||||||
|
|
||||||
|
After execution, report created, updated, skipped, rejected and conflicted
|
||||||
|
rows, then use the server's persisted readback when available. Destructive
|
||||||
|
operations require explicit confirmation and remain subject to server ACL.
|
||||||
|
|
||||||
|
See `references/write-preflight.md` for the required preflight record.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD safe bulk changes
|
||||||
|
short_description: Plan and validate bulk mutations
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Write preflight
|
||||||
|
|
||||||
|
Before a multi-record write, record:
|
||||||
|
|
||||||
|
`module → scope → match rule → current count → affected count → skipped/ambiguous count → validation result → idempotency key`
|
||||||
|
|
||||||
|
Stop on an ambiguous match, validation failure, conflict or missing readback.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: shd-status-page
|
||||||
|
description: Use when the user asks to inspect SHD status-page monitors, history, groups or incidents, or to change public status monitoring.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD status page
|
||||||
|
|
||||||
|
See `references/status-page-safety.md` for monitor and incident boundaries.
|
||||||
|
|
||||||
|
## Read
|
||||||
|
|
||||||
|
- Resolve the status-page scope and monitor IDs before reporting health.
|
||||||
|
- Read current monitors and history; distinguish current state from historical
|
||||||
|
observations and from an incident declaration.
|
||||||
|
- Report unavailable, stale or unauthorized monitors explicitly.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
Publishing an incident, creating/updating/deleting a group or monitor changes
|
||||||
|
an externally visible monitoring surface. Require an explicit request, exact
|
||||||
|
scope and current readback. Do not publish an incident to test the integration
|
||||||
|
and do not infer an outage from one missing response.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD status page"
|
||||||
|
short_description: "Inspect monitors and publish incidents safely"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Status-page safety contract
|
||||||
|
|
||||||
|
Read with `shd_get_status_page_monitors` and
|
||||||
|
`shd_get_status_page_monitor_history`. Mutations are
|
||||||
|
`shd_publish_status_page_incident`, `shd_create_status_page_group`,
|
||||||
|
`shd_update_status_page_group`, `shd_delete_status_page_group`,
|
||||||
|
`shd_create_status_page_monitor`, `shd_update_status_page_monitor` and
|
||||||
|
`shd_delete_status_page_monitor`.
|
||||||
|
|
||||||
|
Before a mutation, resolve the canonical monitor/group IDs, read the latest
|
||||||
|
state and show the public scope, message, duration or target being changed.
|
||||||
|
Report server validation and final status. Deletion and incident publication
|
||||||
|
are consequential actions and must never be inferred from a question asking for
|
||||||
|
diagnosis.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
name: shd-task-workflow
|
||||||
|
description: Use when the user asks to find, inspect, create, update, reschedule, archive, or delete SHD tasks or task-project data.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD task workflow
|
||||||
|
|
||||||
|
See `references/task-contract.md` for identifier resolution, conflict handling
|
||||||
|
and mutation readback requirements.
|
||||||
|
|
||||||
|
## Read and resolve
|
||||||
|
|
||||||
|
- Use `shd_list_task_projects` when the request concerns a task project or its
|
||||||
|
status.
|
||||||
|
- Use `shd_list_tasks` with `project_code`, `project_id`, `q`, `archived`,
|
||||||
|
`limit` or `offset` to find tasks.
|
||||||
|
- Use `shd_get_task` for one resolved task and
|
||||||
|
`shd_get_task_project_meta` for statuses, tags, sprints, views and custom
|
||||||
|
fields.
|
||||||
|
- Read comments or attachments only when they are relevant to the request.
|
||||||
|
- After `shd_list_tasks` returns, always pass normalized task records to
|
||||||
|
`shd_render_tasks_widget` before the final response when the host supports
|
||||||
|
MCP Apps UI. Use the structured task list or plain text only when the render
|
||||||
|
tool is unavailable or returns an error.
|
||||||
|
|
||||||
|
Do not create a task from an ambiguous project name or duplicate a task that
|
||||||
|
already matches the request.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
Only mutate when the user explicitly asks for the exact operation:
|
||||||
|
|
||||||
|
- create with `shd_create_task`;
|
||||||
|
- update fields with `shd_update_task`;
|
||||||
|
- change status with `shd_update_task_status`;
|
||||||
|
- archive with `shd_archive_task`;
|
||||||
|
- delete with `shd_delete_task` and the server's explicit confirmation contract.
|
||||||
|
|
||||||
|
Before updating, pass the latest `updated_at` when the tool accepts it. Use an
|
||||||
|
idempotency key when the tool schema accepts one. If the server reports a
|
||||||
|
conflict, stop and show the current state; do not overwrite it by guessing.
|
||||||
|
|
||||||
|
Report the task ID, changed fields, server validation, conflict state and final
|
||||||
|
status. Never treat a suggested change as authorization to execute it.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD task workflow
|
||||||
|
short_description: Read and safely update tasks
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Task workflow contract
|
||||||
|
|
||||||
|
Resolve task context before acting:
|
||||||
|
|
||||||
|
- use a canonical task-project identifier when the server returns one;
|
||||||
|
- search by explicit project, task ID or bounded query rather than guessing
|
||||||
|
from a display name;
|
||||||
|
- read task-project metadata before changing status, sprint, tags or custom
|
||||||
|
fields;
|
||||||
|
- retain `updated_at`, version or equivalent conflict data from the latest
|
||||||
|
read whenever the mutation schema supports it.
|
||||||
|
|
||||||
|
For a write, report the exact operation, task ID, changed fields, idempotency
|
||||||
|
key when used, server validation, conflict result and readback state. A
|
||||||
|
conflict or ambiguous match is a stop condition: show the current state and
|
||||||
|
ask for a narrower target instead of overwriting it.
|
||||||
|
|
||||||
|
Delete and archive operations require their server-side confirmation contract.
|
||||||
|
Never treat a proposed task change or a natural-language suggestion as write
|
||||||
|
authorization.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: shd-terms-contracts
|
||||||
|
description: Use when the user asks to inspect, compare, discuss, revise or restore SHD Terms documents and contract points.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD Terms and contracts
|
||||||
|
|
||||||
|
See `references/terms-contract.md` for revision, completeness and discussion
|
||||||
|
rules.
|
||||||
|
|
||||||
|
## Inspect
|
||||||
|
|
||||||
|
- Resolve the canonical Terms document and project context.
|
||||||
|
- Read completeness, current revision and relevant history before comparing or
|
||||||
|
summarizing contractual content.
|
||||||
|
- Keep document text, comments, point discussions and completeness findings
|
||||||
|
distinct; identify unavailable sections instead of filling them in.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
Restoring a revision or adding a contract comment/discussion point requires an
|
||||||
|
explicit request and the exact document/point scope. Preserve the latest
|
||||||
|
revision or conflict token where available, report the created comment or
|
||||||
|
restored revision, and never present a draft as an approved contract.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "SHD Terms and contracts"
|
||||||
|
short_description: "Review revisions and contract completeness"
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Terms and contract contract
|
||||||
|
|
||||||
|
Read with `shd_list_terms_documents`, `shd_get_terms_document`,
|
||||||
|
`shd_get_terms_document_completeness`, `shd_list_terms_document_revisions` and
|
||||||
|
`shd_get_terms_document_revision`. Use `shd_terms_description_comment` and
|
||||||
|
`shd_terms_point_discussion` only for explicit comment/discussion requests.
|
||||||
|
|
||||||
|
`shd_restore_terms_document_revision` is a mutation: resolve the document and
|
||||||
|
revision, read current state, require confirmation and report readback. Never
|
||||||
|
restore merely because an older revision looks cleaner, and never confuse
|
||||||
|
completeness analysis with legal approval.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: shd-wiki-management
|
||||||
|
description: Use when the user asks to search, read, compare, create or update SHD Wiki, notes, pages, collections, databases, revisions or attachments.
|
||||||
|
---
|
||||||
|
|
||||||
|
# SHD Wiki and notes
|
||||||
|
|
||||||
|
## Read path
|
||||||
|
|
||||||
|
1. Resolve the note space and page tree with `shd_list_note_spaces`,
|
||||||
|
`shd_get_note_space_tree` and `shd_list_note_pages`.
|
||||||
|
2. Search with `shd_search_note_blocks`, then read the selected page with
|
||||||
|
`shd_read_note_document` or `shd_get_note_page`.
|
||||||
|
3. Use revision and operation tools to compare versions before proposing a
|
||||||
|
replacement or restore.
|
||||||
|
4. Keep attachments, backlinks, comments, database records and page content as
|
||||||
|
separate objects in the report.
|
||||||
|
|
||||||
|
## Mutations
|
||||||
|
|
||||||
|
Creating, replacing, moving, archiving, deleting, importing, granting members
|
||||||
|
or changing review state requires an exact explicit request. Read the current
|
||||||
|
object first, preserve the latest revision/version and use the dedicated
|
||||||
|
operation contract. Read back the persisted result after a successful write.
|
||||||
|
|
||||||
|
See `references/wiki-safety.md` for content and revision boundaries.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface:
|
||||||
|
display_name: SHD Wiki and notes
|
||||||
|
short_description: Pages, revisions and note databases
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: false
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Wiki safety
|
||||||
|
|
||||||
|
- A search result is not the full page.
|
||||||
|
- A revision diff is not a write proposal.
|
||||||
|
- A replacement must preserve the selected page and revision identifiers.
|
||||||
|
- Deleting a page, collection, database or attachment requires an exact target
|
||||||
|
and the server's explicit confirmation fields.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>SHD MCP</title>
|
||||||
|
<style data-shd-widget-theme="shared">
|
||||||
|
__SHD_WIDGET_THEME_CSS__
|
||||||
|
</style>
|
||||||
|
<style data-shd-widget-write-actions="css">
|
||||||
|
__SHD_WIDGET_ACTIONS_CSS__
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="shell" data-shd-widget-engine="shd-universal-v1" data-widget-module="__SHD_WIDGET_MODULE__"><section class="card">
|
||||||
|
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
|
||||||
|
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
|
||||||
|
<div class="status" id="status" role="status" aria-live="polite"></div>
|
||||||
|
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
|
||||||
|
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
|
||||||
|
</section></main>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
'use strict';
|
||||||
|
var DEFAULT_MODULE='__SHD_WIDGET_MODULE__';
|
||||||
|
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние',yes:'Да',no:'Нет'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health',yes:'Yes',no:'No'}};
|
||||||
|
var FIELD_LABELS={ru:{id:'ID',key:'Ключ',project_id:'ID проекта',task_project_id:'ID проекта задачи',linked_project_ids:'Связанные проекты',project_code:'Код проекта',base_reference:'Ссылка на базу',base_public_id:'Публичный ID базы',table_public_id:'Публичный ID таблицы',record_public_id:'Публичный ID записи',title:'Название',name:'Название',description:'Описание',status:'Статус',status_id:'ID статуса',is_done:'Завершено',is_complete:'Завершено',section:'Раздел',sort_order:'Порядок сортировки',created_at:'Создано',updated_at:'Обновлено',due_date:'Срок',deadline:'Дедлайн',starts_at:'Начало',ends_at:'Окончание',url:'Ссылка',diagnostic:'Диагностика',message:'Сообщение',type:'Тип',state:'Состояние',phase:'Этап',health:'Состояние',email:'Электронная почта',username:'Имя пользователя',role:'Роль',permissions:'Разрешения',source:'Источник',module:'Модуль',table:'Таблица',fields:'Поля',value:'Значение',stage:'Стадия',approval_status:'Статус согласования',approval_state:'Состояние согласования',priority:'Приоритет',assignee:'Исполнитель',owner:'Владелец',type:'Тип',category:'Категория',permissions:'Разрешения',permission:'Разрешение',read:'Чтение',write:'Запись',manage:'Управление',admin:'Администратор',view:'Просмотр',create:'Создание',update:'Изменение',delete:'Удаление',error:'Ошибка',warning:'Предупреждение',message:'Сообщение',path:'Путь',link:'Ссылка',amount:'Сумма',total:'Итого',quantity:'Количество',available_quantity:'Доступное количество',stock:'Остаток',planned:'План',planned_hours:'Плановые часы',capacity:'Ёмкость',capacity_hours:'Часы ёмкости',utilization:'Загрузка',utilization_percent:'Загрузка, %',overload:'Перегрузка',overload_hours:'Часы перегрузки',score:'Оценка',progress:'Прогресс',completeness_score:'Оценка полноты',completion_percent:'Полнота, %',availability_percent:'Доступность, %',record_count:'Количество записей',revision:'Ревизия',pages:'Страницы',page_count:'Количество страниц',date:'Дата',is_archived:'В архиве',low_stock:'Мало на складе',critical:'Критический',meta:'Метаданные'},en:{id:'ID',key:'Key',project_id:'Project ID',task_project_id:'Task project ID',linked_project_ids:'Linked projects',project_code:'Project code',base_reference:'Base reference',base_public_id:'Public base ID',table_public_id:'Public table ID',record_public_id:'Public record ID',title:'Title',name:'Name',description:'Description',status:'Status',status_id:'Status ID',is_done:'Completed',is_complete:'Completed',section:'Section',sort_order:'Sort order',created_at:'Created',updated_at:'Updated',due_date:'Due date',deadline:'Deadline',starts_at:'Starts',ends_at:'Ends',url:'URL',diagnostic:'Diagnostics',message:'Message',type:'Type',state:'State',phase:'Phase',health:'Health',email:'Email',username:'Username',role:'Role',permissions:'Permissions',source:'Source',module:'Module',table:'Table',fields:'Fields',value:'Value',stage:'Stage',approval_status:'Approval status',approval_state:'Approval state',priority:'Priority',assignee:'Assignee',owner:'Owner',type:'Type',category:'Category',permissions:'Permissions',permission:'Permission',read:'Read',write:'Write',manage:'Manage',admin:'Admin',view:'View',create:'Create',update:'Update',delete:'Delete',error:'Error',warning:'Warning',message:'Message',path:'Path',link:'Link',amount:'Amount',total:'Total',quantity:'Quantity',available_quantity:'Available quantity',stock:'Stock',planned:'Planned',planned_hours:'Planned hours',capacity:'Capacity',capacity_hours:'Capacity hours',utilization:'Utilization',utilization_percent:'Utilization, %',overload:'Overload',overload_hours:'Overload hours',score:'Score',progress:'Progress',completeness_score:'Completeness score',completion_percent:'Completeness, %',availability_percent:'Availability, %',record_count:'Record count',revision:'Revision',pages:'Pages',page_count:'Page count',date:'Date',is_archived:'Archived',low_stock:'Low stock',critical:'Critical',meta:'Metadata'}};
|
||||||
|
var VALUE_LABELS={ru:{Tasks_Status_Todo:'К выполнению',Tasks_Status_Done:'Завершено',Tasks_Status_In_Progress:'В работе',Tasks_Status_Review:'На проверке',Modules_Done:'Завершено',todo:'К выполнению',to_do:'К выполнению','to do':'К выполнению',in_progress:'В работе','in progress':'В работе','in-progress':'В работе',in_progress_status:'В работе',done:'Завершено',completed:'Завершено',complete:'Завершено',closed:'Закрыто',active:'Активно',pending:'Ожидает',review:'На проверке',approved:'Согласовано',rejected:'Отклонено',archived:'В архиве',not_set:'Не задано'},en:{Tasks_Status_Todo:'To do',Tasks_Status_Done:'Done',Tasks_Status_In_Progress:'In progress',Tasks_Status_Review:'In review',Modules_Done:'Done',todo:'To do',to_do:'To do','to do':'To do',in_progress:'In progress','in progress':'In progress','in-progress':'In progress',in_progress_status:'In progress',done:'Done',completed:'Completed',complete:'Complete',closed:'Closed',active:'Active',pending:'Pending',review:'In review',approved:'Approved',rejected:'Rejected',archived:'Archived',not_set:'Not set'}};
|
||||||
|
var MODULE_CONFIG=__SHD_WIDGET_CONFIG__;
|
||||||
|
var M=MODULE_CONFIG.copy;
|
||||||
|
var S=MODULE_CONFIG.defaults;
|
||||||
|
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',timezone:'',busy:false};
|
||||||
|
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
|
||||||
|
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
|
||||||
|
function t(k){return(C[state.lang]||C.ru)[k]||k}
|
||||||
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
|
||||||
|
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
|
||||||
|
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
|
||||||
|
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
|
||||||
|
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id='widget-'+nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
|
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
|
||||||
|
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
|
||||||
|
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
|
||||||
|
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
|
||||||
|
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
|
||||||
|
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
|
||||||
|
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
|
||||||
|
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
|
||||||
|
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
|
||||||
|
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
|
||||||
|
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
|
||||||
|
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
|
||||||
|
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
|
||||||
|
function dateOnlyText(raw){var parts=raw.split('-').map(Number);if(parts.length!==3||parts.some(function(part){return!Number.isFinite(part)}))return null;var date=new Date(Date.UTC(parts[0],parts[1]-1,parts[2],12));try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeZone:'UTC'}).format(date)}catch(err){return null}}
|
||||||
|
function dateText(x){if(x===null||x===undefined||x==='')return'—';var n;if(x instanceof Date)n=x.getTime();else if(typeof x==='number')n=Math.abs(x)<100000000000?x*1000:x;else{var raw=String(x).trim();if(/^\d{4}-\d{2}-\d{2}$/.test(raw)){var only=dateOnlyText(raw);if(only)return only}var numeric=Number(raw);n=raw!==''&&Number.isFinite(numeric)?(Math.abs(numeric)<100000000000?numeric*1000:numeric):Date.parse(raw);if(Number.isNaN(n)&&raw)n=Date.parse(raw.replace(/([+-]\d{2}):(\d{2})$/,'$1$2'))}if(!Number.isFinite(n))return String(x);var options={dateStyle:'medium',timeStyle:'medium'};if(state.timezone)options.timeZone=state.timezone;try{return new Intl.DateTimeFormat(state.lang,options).format(new Date(n))}catch(err){return String(x)}}
|
||||||
|
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
|
||||||
|
function fieldLabel(key){var raw=String(key||'');var labels=FIELD_LABELS[state.lang]||FIELD_LABELS.ru;return labels[raw]||raw.replace(/[_-]+/g,' ')}
|
||||||
|
function dateKey(key){return['date','time','timestamp','source_timestamp','from','to','start','end','bucket_start','created_at','updated_at','due_date','deadline','starts_at','ends_at','checked_at','sent_at','published_at'].indexOf(String(key||''))!==-1}
|
||||||
|
function detailText(x,key){if(x===null||x===undefined||x==='')return'—';if(typeof x==='boolean')return x?t('yes'):t('no');if(Array.isArray(x))return'['+x.map(function(v){return detailText(v,key)}).join(', ')+']';if(obj(x))return'{'+Object.keys(x).map(function(k){return fieldLabel(k)+': '+detailText(x[k],k)}).join(', ')+'}';var raw=String(x),labels=VALUE_LABELS[state.lang]||VALUE_LABELS.ru;if(dateKey(key)&&!Number.isNaN(Date.parse(raw)))return dateText(raw);return labels[raw]||labels[raw.toLowerCase()]||raw}
|
||||||
|
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+detailText(k,'status'));e.summary.append(n)})}
|
||||||
|
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
|
||||||
|
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
|
||||||
|
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
|
||||||
|
function recordAttrs(node,x){if(!node||!x)return node;var recordId=str(x.id);if(recordId)node.dataset.shdRecordId=recordId;var recordTitle=str(x.title);if(recordTitle)node.dataset.shdRecordTitle=recordTitle;return node}
|
||||||
|
function interactiveRecord(node,x){recordAttrs(node,x);if(node.tagName!=='BUTTON'&&node.tagName!=='A'){node.classList.add('record');node.tabIndex=0;node.setAttribute('role','button');node.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}}}return node}
|
||||||
|
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';recordAttrs(b,x);b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',detailText(x.status,'status'),'pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',detailText(v),'visual-value'));b.onclick=function(){details(x)};return b}
|
||||||
|
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
|
||||||
|
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',detailText(g,state.config.groupBy||'status')),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
|
||||||
|
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
|
||||||
|
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Object.keys(groups).reduce(function(current,g){return Math.max(current,groups[g])},0),wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',detailText(g,state.config.groupBy||'stage')),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
|
||||||
|
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
|
||||||
|
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
|
||||||
|
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(detailText(g,keys[0]),counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
|
||||||
|
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
|
||||||
|
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=interactiveRecord(make('article',undefined,'progress-card'),x);card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
|
||||||
|
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
|
||||||
|
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day'),label=/^\d{4}-\d{2}-\d{2}$/.test(d)?dateOnlyText(d):d;day.append(make('div',label||d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
|
||||||
|
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
|
||||||
|
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return fieldLabel(k)+(typeof p[k]==='string'?'='+detailText(p[k],k):'')}).join(', ')||'—';if(Array.isArray(p))return p.map(function(k){return detailText(k,'permissions')}).join(', ')||'—';return detailText(p,'permissions')||'—'}
|
||||||
|
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=interactiveRecord(make('tr'),x);row.append(make('td',memberName(x)),make('td',detailText(field(x,['role','organization_role','type']),'role')||'—'),make('td',detailText(status(x),'status')||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
|
||||||
|
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
|
||||||
|
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
|
||||||
|
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
|
||||||
|
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=interactiveRecord(make('article',undefined,'preview-card'),x);card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',fieldLabel(k)+': '),make('span',detailText(x[k],k)));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
|
||||||
|
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
|
||||||
|
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';recordAttrs(r,x);r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',detailText(x.status,'status'),'pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',detailText(x.value,'value')));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
|
||||||
|
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
|
||||||
|
function setTimezone(p){var source=payload(p)||{},data=source.data!==undefined?source.data:source,timezone=data&&data.timezone||source.timezone;if(typeof timezone==='string'&&timezone)state.timezone=timezone}
|
||||||
|
function render(x){var p=unwrap(x);applyConfig(p);setTimezone(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});window.__SHD_WIDGET_STATE__=p;renderRows();window.dispatchEvent(new CustomEvent('shd-widget-data',{detail:p}))}
|
||||||
|
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
|
||||||
|
function add(k,v,key){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v,key),'value'));e.grid.append(d)}
|
||||||
|
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id,'id');if(x.status)add(t('status'),x.status,'status');if(x.date)add(t('date'),dateText(x.date),'date');if(x.value)add(t('value'),x.value,'value');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(fieldLabel(k),x[k],k)});e.details.hidden=false}
|
||||||
|
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;show(x);if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(fieldLabel(q),d[q],q)});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
|
||||||
|
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
|
||||||
|
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
|
||||||
|
function init(){return req('ui/initialize',{protocolVersion:'2026-01-26',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
|
||||||
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
|
||||||
|
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
<script data-shd-widget-write-actions="js">
|
||||||
|
__SHD_WIDGET_ACTIONS_JS__
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,142 @@
|
|||||||
|
.shd-widget-actions {
|
||||||
|
min-width: 0;
|
||||||
|
padding: var(--shd-space-5, 20px);
|
||||||
|
border: 1px solid var(--shd-line-strong, var(--line, rgba(31, 41, 55, .16)));
|
||||||
|
border-radius: var(--shd-radius-md, 14px);
|
||||||
|
background: var(--shd-surface-raised, var(--surface, #e8edf2));
|
||||||
|
color: var(--shd-ink, var(--ink, #1d1f25));
|
||||||
|
box-shadow: var(--shd-shadow-soft, none);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--shd-space-3, 12px);
|
||||||
|
margin-bottom: var(--shd-space-3, 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-selection {
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--shd-muted, var(--muted, #616670));
|
||||||
|
font-size: 12px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-select,
|
||||||
|
.shd-widget-actions-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 9px 10px;
|
||||||
|
border: 1px solid var(--shd-line-strong, var(--line, rgba(31, 41, 55, .16)));
|
||||||
|
border-radius: var(--shd-radius-sm, 10px);
|
||||||
|
background: var(--shd-bg, var(--bg, #f8fafc));
|
||||||
|
color: var(--shd-ink, var(--ink, #1d1f25));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-select:focus,
|
||||||
|
.shd-widget-actions-input:focus {
|
||||||
|
border-color: var(--shd-accent, var(--accent, #535963));
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: 0 0 0 3px var(--shd-accent-soft, var(--soft, rgba(75, 85, 99, .12)));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
gap: var(--shd-space-3, 12px);
|
||||||
|
margin-top: var(--shd-space-3, 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-field {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--shd-space-1, 4px);
|
||||||
|
color: var(--shd-muted, var(--muted, #616670));
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-field:has(textarea) {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-field input[type="checkbox"] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
accent-color: var(--shd-accent, var(--accent, #535963));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-confirm {
|
||||||
|
display: flex;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--shd-space-2, 8px);
|
||||||
|
color: var(--shd-ink, var(--ink, #1d1f25));
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-warning,
|
||||||
|
.shd-widget-actions-hint {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
color: var(--shd-muted, var(--muted, #616670));
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-danger {
|
||||||
|
color: var(--shd-danger, var(--bad, #b42318));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-status {
|
||||||
|
min-height: 18px;
|
||||||
|
margin-top: var(--shd-space-3, 12px);
|
||||||
|
color: var(--shd-muted, var(--muted, #616670));
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-status-error {
|
||||||
|
color: var(--shd-danger, var(--bad, #b42318));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--shd-space-2, 8px);
|
||||||
|
margin-top: var(--shd-space-4, 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-buttons button {
|
||||||
|
padding: 9px 12px;
|
||||||
|
border: 1px solid var(--shd-line-strong, var(--line, rgba(31, 41, 55, .16)));
|
||||||
|
border-radius: var(--shd-radius-sm, 10px);
|
||||||
|
background: var(--shd-surface-subtle, var(--bg, #f8fafc));
|
||||||
|
color: var(--shd-ink, var(--ink, #1d1f25));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-buttons button:hover {
|
||||||
|
border-color: var(--shd-accent, var(--accent, #535963));
|
||||||
|
background: var(--shd-surface, var(--hover, #e1e8ef));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-buttons button:disabled {
|
||||||
|
cursor: wait;
|
||||||
|
opacity: .62;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.shd-widget-actions-field:has(textarea) {
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-header {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shd-widget-actions-selection {
|
||||||
|
display: block;
|
||||||
|
margin-top: var(--shd-space-1, 4px);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,454 @@
|
|||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.__SHD_WIDGET_WRITE_ACTIONS__) return;
|
||||||
|
window.__SHD_WIDGET_WRITE_ACTIONS__ = true;
|
||||||
|
|
||||||
|
var copy = {
|
||||||
|
ru: {
|
||||||
|
actions: 'Действия',
|
||||||
|
chooseAction: 'Выберите действие',
|
||||||
|
selectRecord: 'Сначала выберите запись.',
|
||||||
|
submit: 'Сохранить',
|
||||||
|
cancel: 'Отмена',
|
||||||
|
confirm: 'Подтверждаю действие',
|
||||||
|
warning: 'Проверьте данные перед записью.',
|
||||||
|
destructive: 'Операция может изменить или удалить данные.',
|
||||||
|
required: 'Заполните обязательное поле.',
|
||||||
|
invalidJson: 'Введите корректный JSON.',
|
||||||
|
success: 'Изменения сохранены.',
|
||||||
|
failure: 'Не удалось сохранить изменения.',
|
||||||
|
loading: 'Сохраняю…',
|
||||||
|
noActions: 'Для этого виджета нет доступных действий.',
|
||||||
|
refreshed: 'Список обновлён.',
|
||||||
|
fieldValue: 'Значение',
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
actions: 'Actions',
|
||||||
|
chooseAction: 'Choose an action',
|
||||||
|
selectRecord: 'Select a record first.',
|
||||||
|
submit: 'Save',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
confirm: 'I confirm this action',
|
||||||
|
warning: 'Review the values before writing.',
|
||||||
|
destructive: 'This operation may change or remove data.',
|
||||||
|
required: 'Fill in the required field.',
|
||||||
|
invalidJson: 'Enter valid JSON.',
|
||||||
|
success: 'Changes saved.',
|
||||||
|
failure: 'Could not save changes.',
|
||||||
|
loading: 'Saving…',
|
||||||
|
noActions: 'No actions are available for this widget.',
|
||||||
|
refreshed: 'List refreshed.',
|
||||||
|
fieldValue: 'Value',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var state = {
|
||||||
|
widget: {},
|
||||||
|
records: [],
|
||||||
|
selected: null,
|
||||||
|
action: null,
|
||||||
|
inputs: {},
|
||||||
|
busy: false,
|
||||||
|
lang: String(document.documentElement.lang || 'ru').toLowerCase().indexOf('ru') === 0 ? 'ru' : 'en',
|
||||||
|
};
|
||||||
|
var pending = new Map();
|
||||||
|
var nextRequestId = 1;
|
||||||
|
var elements = {};
|
||||||
|
|
||||||
|
function t(key) {
|
||||||
|
return (copy[state.lang] && copy[state.lang][key]) || copy.en[key] || key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function object(value) {
|
||||||
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize(value) {
|
||||||
|
if (!value) return null;
|
||||||
|
if (value.structuredContent) return normalize(value.structuredContent);
|
||||||
|
if (value.toolOutput) return normalize(value.toolOutput);
|
||||||
|
if (value.toolInput) return normalize(value.toolInput);
|
||||||
|
if (value.arguments && !value.data && !value.items && !value.projects) return normalize(value.arguments);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function widgetFrom(value) {
|
||||||
|
var payload = normalize(value) || {};
|
||||||
|
var meta = object(payload.meta) ? payload.meta : {};
|
||||||
|
return object(meta.widget) ? meta.widget : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordsFrom(value) {
|
||||||
|
var payload = normalize(value) || {};
|
||||||
|
var data = Array.isArray(payload.items) ? payload.items : payload.projects;
|
||||||
|
if (!Array.isArray(data)) data = payload.data;
|
||||||
|
if (data && !Array.isArray(data) && Array.isArray(data.data)) data = data.data;
|
||||||
|
return Array.isArray(data) ? data.filter(function (item) { return object(item); }) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPath(value, path) {
|
||||||
|
if (!value || path === undefined || path === null) return undefined;
|
||||||
|
var parts = String(path).split('.');
|
||||||
|
var current = value;
|
||||||
|
for (var index = 0; index < parts.length; index += 1) {
|
||||||
|
if (current === undefined || current === null) return undefined;
|
||||||
|
current = current[parts[index]];
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstValue(paths) {
|
||||||
|
var list = Array.isArray(paths) ? paths : [paths];
|
||||||
|
var sources = [state.selected, state.widget.sourceArgs, state.widget.listArgs];
|
||||||
|
for (var sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
|
||||||
|
var sourceObject = sources[sourceIndex];
|
||||||
|
if (!sourceObject) continue;
|
||||||
|
for (var pathIndex = 0; pathIndex < list.length; pathIndex += 1) {
|
||||||
|
var value = readPath(sourceObject, list[pathIndex]);
|
||||||
|
if (value !== undefined && value !== null && value !== '') return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(value) {
|
||||||
|
if (value === undefined || value === null) return '';
|
||||||
|
if (typeof value === 'object') return JSON.stringify(value);
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function label(value) {
|
||||||
|
if (object(value)) return value[state.lang] || value.ru || value.en || t('fieldValue');
|
||||||
|
return String(value || t('fieldValue'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionLabel(action) {
|
||||||
|
return label(action && action.label) || action.id || action.tool || t('actions');
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasTargets(action) {
|
||||||
|
return Boolean(action && ((Array.isArray(action.targets) && action.targets.length) || action.target));
|
||||||
|
}
|
||||||
|
|
||||||
|
function actionList() {
|
||||||
|
return Array.isArray(state.widget.actions) ? state.widget.actions.filter(function (action) {
|
||||||
|
return action && action.tool;
|
||||||
|
}) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function request(method, params) {
|
||||||
|
var openai = typeof window !== 'undefined' ? window.openai : undefined;
|
||||||
|
if (method === 'tools/call' && openai && typeof openai.callTool === 'function') {
|
||||||
|
return openai.callTool(params.name, params.arguments || {});
|
||||||
|
}
|
||||||
|
var id = 'actions-' + nextRequestId++;
|
||||||
|
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params || {} }, '*');
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var timeout = window.setTimeout(function () {
|
||||||
|
pending.delete(id);
|
||||||
|
reject(new Error(t('failure')));
|
||||||
|
}, 15000);
|
||||||
|
pending.set(id, { resolve: resolve, reject: reject, timeout: timeout });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrap(value) {
|
||||||
|
var payload = normalize(value);
|
||||||
|
if (value && value.isError) throw new Error(payload && payload.error ? text(payload.error) : t('failure'));
|
||||||
|
if (payload && payload.error) throw new Error(text(payload.error));
|
||||||
|
return payload || value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function make(tag, value, className) {
|
||||||
|
var node = document.createElement(tag);
|
||||||
|
if (className) node.className = className;
|
||||||
|
if (value !== undefined) node.textContent = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensurePanel() {
|
||||||
|
if (elements.panel) return;
|
||||||
|
var root = document.querySelector('main') || document.body;
|
||||||
|
var panel = make('section', undefined, 'shd-widget-actions');
|
||||||
|
panel.hidden = true;
|
||||||
|
panel.setAttribute('aria-live', 'polite');
|
||||||
|
var header = make('div', undefined, 'shd-widget-actions-header');
|
||||||
|
header.append(make('strong', t('actions')), make('span', '', 'shd-widget-actions-selection'));
|
||||||
|
var select = document.createElement('select');
|
||||||
|
select.className = 'shd-widget-actions-select';
|
||||||
|
select.setAttribute('aria-label', t('actions'));
|
||||||
|
var form = make('form', undefined, 'shd-widget-actions-form');
|
||||||
|
var status = make('div', '', 'shd-widget-actions-status');
|
||||||
|
var buttons = make('div', undefined, 'shd-widget-actions-buttons');
|
||||||
|
var submit = make('button', t('submit'), 'shd-widget-actions-submit');
|
||||||
|
submit.type = 'submit';
|
||||||
|
var cancel = make('button', t('cancel'), 'shd-widget-actions-cancel');
|
||||||
|
cancel.type = 'button';
|
||||||
|
buttons.append(submit, cancel);
|
||||||
|
panel.append(header, select, form, status, buttons);
|
||||||
|
root.append(panel);
|
||||||
|
elements = { panel: panel, selection: header.lastChild, select: select, form: form, status: status, submit: submit, cancel: cancel };
|
||||||
|
select.addEventListener('change', function () {
|
||||||
|
var selected = actionList().find(function (action) { return action.id === select.value; });
|
||||||
|
state.action = selected || null;
|
||||||
|
renderForm();
|
||||||
|
});
|
||||||
|
cancel.addEventListener('click', function () {
|
||||||
|
state.action = null;
|
||||||
|
select.value = '';
|
||||||
|
renderForm();
|
||||||
|
});
|
||||||
|
form.addEventListener('submit', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
submitAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputValue(fieldConfig) {
|
||||||
|
var value = firstValue(fieldConfig.source || []);
|
||||||
|
if (value === undefined && fieldConfig.default !== undefined) value = fieldConfig.default;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInput(fieldConfig) {
|
||||||
|
var type = fieldConfig.type || 'text';
|
||||||
|
var input;
|
||||||
|
if (type === 'textarea' || type === 'json') {
|
||||||
|
input = document.createElement('textarea');
|
||||||
|
input.rows = type === 'json' ? 4 : 3;
|
||||||
|
if (type === 'json') input.placeholder = '{ }';
|
||||||
|
} else if (type === 'select') {
|
||||||
|
input = document.createElement('select');
|
||||||
|
(fieldConfig.options || []).forEach(function (option) {
|
||||||
|
var item = document.createElement('option');
|
||||||
|
item.value = String(option[0]);
|
||||||
|
item.textContent = state.lang === 'ru' ? String(option[1]) : String(option[2] || option[1]);
|
||||||
|
input.append(item);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
input = document.createElement('input');
|
||||||
|
input.type = type === 'number' || type === 'email' ? type : 'text';
|
||||||
|
}
|
||||||
|
input.className = 'shd-widget-actions-input';
|
||||||
|
input.name = fieldConfig.name;
|
||||||
|
var value = inputValue(fieldConfig);
|
||||||
|
if (type === 'checkbox') {
|
||||||
|
input.type = 'checkbox';
|
||||||
|
input.checked = value === true || value === 'true' || value === 1 || value === '1';
|
||||||
|
} else if (value !== undefined && value !== null) {
|
||||||
|
input.value = type === 'json' && typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value);
|
||||||
|
}
|
||||||
|
if (fieldConfig.required) input.required = true;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderForm() {
|
||||||
|
ensurePanel();
|
||||||
|
var selectedActionId = state.action ? state.action.id : '';
|
||||||
|
elements.select.replaceChildren();
|
||||||
|
var placeholder = document.createElement('option');
|
||||||
|
placeholder.value = '';
|
||||||
|
placeholder.textContent = t('chooseAction');
|
||||||
|
elements.select.append(placeholder);
|
||||||
|
actionList().forEach(function (action) {
|
||||||
|
var option = document.createElement('option');
|
||||||
|
option.value = action.id;
|
||||||
|
option.textContent = actionLabel(action);
|
||||||
|
elements.select.append(option);
|
||||||
|
});
|
||||||
|
elements.select.value = selectedActionId;
|
||||||
|
elements.select.hidden = actionList().length === 0;
|
||||||
|
elements.form.replaceChildren();
|
||||||
|
elements.status.textContent = '';
|
||||||
|
elements.submit.disabled = state.busy;
|
||||||
|
var action = state.action;
|
||||||
|
if (!action) {
|
||||||
|
elements.selection.textContent = state.selected ? text(state.selected.title || state.selected.name || state.selected.id || '') : '';
|
||||||
|
elements.submit.hidden = true;
|
||||||
|
elements.cancel.hidden = true;
|
||||||
|
if (actionList().length && !state.selected && actionList().every(hasTargets)) {
|
||||||
|
elements.form.append(make('div', t('selectRecord'), 'shd-widget-actions-hint'));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elements.selection.textContent = state.selected ? text(state.selected.title || state.selected.name || state.selected.id || '') : '';
|
||||||
|
elements.submit.hidden = false;
|
||||||
|
elements.cancel.hidden = false;
|
||||||
|
if (hasTargets(action) && !state.selected) elements.form.append(make('div', t('selectRecord'), 'shd-widget-actions-hint'));
|
||||||
|
(action.fields || []).forEach(function (fieldConfig) {
|
||||||
|
if (!fieldConfig || !fieldConfig.name || fieldConfig.generated || fieldConfig.type === 'hidden') return;
|
||||||
|
var wrapper = make('label', undefined, 'shd-widget-actions-field');
|
||||||
|
wrapper.append(make('span', label(fieldConfig.label)));
|
||||||
|
wrapper.append(createInput(fieldConfig));
|
||||||
|
elements.form.append(wrapper);
|
||||||
|
});
|
||||||
|
if (action.confirm) {
|
||||||
|
var confirmLabel = make('label', undefined, 'shd-widget-actions-confirm');
|
||||||
|
var confirm = document.createElement('input');
|
||||||
|
confirm.type = 'checkbox';
|
||||||
|
confirm.name = '__confirm';
|
||||||
|
confirm.required = true;
|
||||||
|
confirmLabel.append(confirm, make('span', t('confirm')));
|
||||||
|
elements.form.append(confirmLabel);
|
||||||
|
}
|
||||||
|
var notice = make('div', action.destructive ? t('destructive') : t('warning'), action.destructive ? 'shd-widget-actions-warning shd-widget-actions-danger' : 'shd-widget-actions-warning');
|
||||||
|
elements.form.append(notice);
|
||||||
|
}
|
||||||
|
|
||||||
|
function coerce(value, type) {
|
||||||
|
if (type === 'number') {
|
||||||
|
var number = Number(value);
|
||||||
|
return Number.isFinite(number) ? number : value;
|
||||||
|
}
|
||||||
|
if (type === 'checkbox') return Boolean(value);
|
||||||
|
if (type === 'json') {
|
||||||
|
try { return JSON.parse(value); } catch (error) { throw new Error(t('invalidJson')); }
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function targetType(name) {
|
||||||
|
return /^(task|document|contract|booking|item|monitor|proposal|approval|page|notification|topic|membership|organization|space|status|project)_?id$/.test(name) || name === 'issue_number' || name === 'number' ? 'number' : 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
function targetList(action) {
|
||||||
|
if (Array.isArray(action.targets)) return action.targets;
|
||||||
|
return action.target ? [action.target] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectArguments(action) {
|
||||||
|
var args = Object.assign({}, action.fixed || {});
|
||||||
|
(action.context || []).forEach(function (name) {
|
||||||
|
var value = firstValue([name]);
|
||||||
|
if (value !== undefined && value !== null && value !== '') args[name] = value;
|
||||||
|
});
|
||||||
|
targetList(action).forEach(function (targetConfig) {
|
||||||
|
var value = firstValue(targetConfig.source || targetConfig.name);
|
||||||
|
if (value === undefined || value === null || value === '') throw new Error(t('selectRecord'));
|
||||||
|
args[targetConfig.name] = coerce(value, targetConfig.type || targetType(targetConfig.name));
|
||||||
|
});
|
||||||
|
(action.fields || []).forEach(function (fieldConfig) {
|
||||||
|
if (!fieldConfig || !fieldConfig.name || fieldConfig.generated || fieldConfig.type === 'hidden') return;
|
||||||
|
var input = Array.prototype.find.call(elements.form.querySelectorAll('[name]'), function (node) {
|
||||||
|
return node.getAttribute('name') === fieldConfig.name;
|
||||||
|
});
|
||||||
|
if (!input) return;
|
||||||
|
var raw = fieldConfig.type === 'checkbox' ? input.checked : input.value.trim();
|
||||||
|
if (raw === '' && !input.checked && !fieldConfig.required) return;
|
||||||
|
if (raw === '' && fieldConfig.required) throw new Error(t('required'));
|
||||||
|
args[fieldConfig.name] = coerce(raw, fieldConfig.type || 'text');
|
||||||
|
});
|
||||||
|
if (action.idempotencyKey) args.idempotency_key = 'shd-widget-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
|
||||||
|
if (action.confirmArg) args.confirm = true;
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(message, error) {
|
||||||
|
elements.status.textContent = message || '';
|
||||||
|
elements.status.className = 'shd-widget-actions-status' + (error ? ' shd-widget-actions-status-error' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitAction() {
|
||||||
|
if (state.busy || !state.action) return;
|
||||||
|
var action = state.action;
|
||||||
|
var args;
|
||||||
|
try { args = collectArguments(action); } catch (error) { setStatus(error.message || t('failure'), true); return; }
|
||||||
|
state.busy = true;
|
||||||
|
elements.submit.disabled = true;
|
||||||
|
elements.cancel.disabled = true;
|
||||||
|
setStatus(t('loading'), false);
|
||||||
|
request('tools/call', { name: action.tool, arguments: args }).then(function (result) {
|
||||||
|
unwrap(result);
|
||||||
|
setStatus(t('success'), false);
|
||||||
|
var refresh = document.getElementById('refresh');
|
||||||
|
if (refresh && typeof refresh.click === 'function') window.setTimeout(function () { refresh.click(); }, 250);
|
||||||
|
}).catch(function (error) {
|
||||||
|
setStatus(error && error.message ? error.message : t('failure'), true);
|
||||||
|
}).finally(function () {
|
||||||
|
state.busy = false;
|
||||||
|
elements.submit.disabled = false;
|
||||||
|
elements.cancel.disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordFromNode(node) {
|
||||||
|
var data = node && node.dataset ? node.dataset : {};
|
||||||
|
var recordId = String(data.shdRecordId || '').toLowerCase();
|
||||||
|
if (recordId) {
|
||||||
|
var exact = state.records.find(function (item) {
|
||||||
|
return text(item.id || item.code || item.key || item.number).toLowerCase() === recordId;
|
||||||
|
});
|
||||||
|
if (exact) return exact;
|
||||||
|
}
|
||||||
|
var recordTitle = String(data.shdRecordTitle || '').toLowerCase();
|
||||||
|
if (recordTitle) {
|
||||||
|
var titled = state.records.find(function (item) {
|
||||||
|
return text(item.title || item.name || item.display_name || item.number).toLowerCase() === recordTitle;
|
||||||
|
});
|
||||||
|
if (titled) return titled;
|
||||||
|
}
|
||||||
|
var ariaLabel = node && node.getAttribute ? String(node.getAttribute('aria-label') || '').toLowerCase() : '';
|
||||||
|
return state.records.find(function (item) {
|
||||||
|
var title = text(item.title || item.name || item.display_name || item.number).toLowerCase();
|
||||||
|
return title && title === ariaLabel;
|
||||||
|
}) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(value) {
|
||||||
|
var payload = normalize(value) || {};
|
||||||
|
var widget = widgetFrom(payload);
|
||||||
|
if (Object.keys(widget).length) state.widget = widget;
|
||||||
|
var records = recordsFrom(payload);
|
||||||
|
if (records.length || Array.isArray(payload.data) || Array.isArray(payload.items) || Array.isArray(payload.projects)) {
|
||||||
|
state.records = records;
|
||||||
|
if (state.selected && !state.records.some(function (item) {
|
||||||
|
var selectedId = text(state.selected.id || state.selected.code || state.selected.key || state.selected.number).toLowerCase();
|
||||||
|
var itemId = text(item.id || item.code || item.key || item.number).toLowerCase();
|
||||||
|
return selectedId && itemId && selectedId === itemId;
|
||||||
|
})) {
|
||||||
|
state.selected = null;
|
||||||
|
state.action = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ensurePanel();
|
||||||
|
var actions = actionList();
|
||||||
|
elements.panel.hidden = actions.length === 0;
|
||||||
|
if (actions.length && !state.action) renderForm();
|
||||||
|
else if (actions.length) renderForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', function (event) {
|
||||||
|
var node = event.target && event.target.closest ? event.target.closest('.record,.project-row,.visual-card,.progress-card,.preview-card,.calendar-card,.timeline-card,.kanban-card') : null;
|
||||||
|
if (!node || (elements.panel && elements.panel.contains(node))) return;
|
||||||
|
var item = recordFromNode(node);
|
||||||
|
if (!item) return;
|
||||||
|
state.selected = item;
|
||||||
|
ensurePanel();
|
||||||
|
renderForm();
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
window.addEventListener('shd-widget-data', function (event) {
|
||||||
|
update(event && event.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.__SHD_WIDGET_ACTIONS_UPDATE__ = update;
|
||||||
|
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
if (event.source !== window.parent) return;
|
||||||
|
var message = event.data;
|
||||||
|
if (!message || message.jsonrpc !== '2.0') return;
|
||||||
|
if (message.id !== undefined && pending.has(message.id)) {
|
||||||
|
var requestState = pending.get(message.id);
|
||||||
|
pending.delete(message.id);
|
||||||
|
window.clearTimeout(requestState.timeout);
|
||||||
|
if (message.error) requestState.reject(new Error(message.error.message || t('failure')));
|
||||||
|
else requestState.resolve(message.result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
ensurePanel();
|
||||||
|
var initial = typeof window !== 'undefined' && (window.__SHD_WIDGET_STATE__ || (window.openai && (window.openai.toolOutput || window.openai.toolInput)));
|
||||||
|
if (initial) update(initial);
|
||||||
|
}());
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-acl-matrix",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/acl-matrix/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-active-projects",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/active-projects/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-activity",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/activity/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-agents-health",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/agents-health/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-agents",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/agents/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-airbase-preview",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/airbase-preview/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-airbase",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/airbase/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-controller-observability",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/controller-observability/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-crm-funnel",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/crm-funnel/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-crm",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"uri": "ui://shd/crm/v1.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-discussions",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/discussions/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"name": "shd-documents-completeness",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"file": "index.html",
|
||||||
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
|
"uri": "ui://shd/documents-completeness/v1.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "../../../../../LICENSE",
|
||||||
|
"source": {
|
||||||
|
"repository": "https://github.com/bulava92/shd-mcp-plugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user