本文へ移動
cccskills
無料GitHub で公開

mcp-tools

Defines how to add Operately MCP tools (API-first wrappers). Use when creating, changing, or reviewing MCP tools under app/lib/operately_web/mcp/tools/, when the user mentions MCP tools, tool catalog, @expected_tool_names, or when exposing a new Operately capability to ChatGPT/Claude MCP clients.

インストール方法を見る

含まれるファイル(2)

  • SKILL.md9.3 KB
  • reference.md16.7 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

MCP Tools

MCP tools are thin wrappers over OperatelyWeb.Api.* handlers. API endpoints are always the source of truth for auth, permissions, business logic, and serialization. MCP tools only declare MCP-facing metadata (schemas, scopes, examples) and adapt string-key JSON arguments into API inputs.

For templates, test skeletons, sort_order bands, and a full walkthrough, see reference.md.

Architecture

flowchart TD
  start[User wants capability X] --> checkTool{MCP tool exists?}
  checkTool -->|yes| stopDone[Stop or extend existing tool]
  checkTool -->|no| checkApi{OperatelyWeb.Api handler exists?}
  checkApi -->|yes| wrapOnly[Write MCP wrapper + MCP tests]
  checkApi -->|no| addApi[Add API handler + register in api.ex]
  addApi --> apiTests[Add API tests with TurboCase]
  apiTests --> wrapOnly
  wrapOnly --> catalog[Update tools_test.exs @expected_tool_names]
  catalog --> done[Run targeted tests]
LayerLocationResponsibility
API (source of truth)app/lib/operately_web/api/Auth, permissions, business logic, serialization
API wrappers (when needed)app/lib/operately_web/api/wrappers/Adapt inputs MCP exposes differently (e.g. hub scope → resource_hub_id)
MCP toolapp/lib/operately_web/mcp/tools/JSON schema, MCP metadata, string-key arg decoding, call API
CatalogRegistry (auto) + tools_test.exs (manual)Discovery and invariant tests

Product rationale: specs/0012-operately-mcp.md § "Shared execution across surfaces".

OAuth scopes

MCP grants use two scopes. There is no mcp:destructive scope — destructive tools stay on mcp:write, matching the CLI write-token model.

Scopesafety_classificationTool access
mcp:read:read_onlyRead-only tools
mcp:write:write or :destructiveAll write and destructive tools

Default scope on connect

Operately.Mcp.Resources.parse_scopes/1 defaults nil, empty string, or empty list to mcp:read only. Clients that need write or destructive access must request mcp:write explicitly in the OAuth scope parameter.

When authoring tools:

  • Read-only → required_scopes: ["mcp:read"], safety_classification: :read_only, read_annotations() (readOnlyHint: true)
  • Write → required_scopes: ["mcp:write"], safety_classification: :write, write_annotations()
  • Destructive → required_scopes: ["mcp:write"], safety_classification: :destructive, write_annotations() plus destructiveHint: true in descriptors (client UX only — not a third scope)

Runtime enforcement in OperatelyWeb.McpController checks token scopes against each tool's required_scopes before dispatch.

Scope

This skill covers adding, changing, and reviewing MCP tool wrappers.

Removing a tool requires deleting the module, its test, and its entry in @expected_tool_names.

Workflow

Always follow these three steps before writing code.

1. Check if the MCP tool already exists

  • Grep app/lib/operately_web/mcp/tools/ by domain and action name
  • Read @expected_tool_names in app/test/operately/mcp/tools_test.exs — the canonical sorted catalog
  • Run the catalog test or inspect OperatelyWeb.Mcp.Tools.list_definitions/0
  • Do not add a duplicate tool with a slightly different name

If a tool already covers the capability, stop or extend it instead of creating a new one.

2. Check if an API endpoint already exists

  • Search app/lib/operately_web/api.ex for query(:…) / mutation(:…) registrations
  • Search app/lib/operately_web/api/{domain}/ for matching modules
  • Check app/lib/operately_web/api/wrappers/ for API module wrappers, which behave like usual endpoints
  • Read sibling MCP tools in the same domain — they show the expected API alias and input mapping

If the API exists and accepts the inputs MCP needs, skip to step 3 (wrapper only).

3. Create endpoint first, then MCP wrapper

When no suitable API exists, always API before MCP:

  1. Add API module under app/lib/operately_web/api/{domain}/{action}.ex
    • Queries: use TurboConnect.Query
    • Mutations: use TurboConnect.Mutation + Action pipeline
  2. Register in app/lib/operately_web/api.ex as query(:…) or mutation(:…)
  3. Add API tests under app/test/operately_web/api/{domain}/{action}_test.exs using OperatelyWeb.TurboCase — cover auth, validation, permissions
  4. Add MCP wrapper under app/lib/operately_web/mcp/tools/{domain}/{action}.ex
  5. Add MCP unit test under app/test/operately_web/mcp/tools/{domain}/{action}_test.exs
  6. Update catalog test: add tool name to @expected_tool_names at the correct position (sort_order, then alphabetical tie-break)

Canonical example: list_task_statuses — API was added first (app/lib/operately_web/api/tasks/list_task_statuses.ex + app/test/operately_web/api/tasks/list_task_statuses_test.exs), then the MCP wrapper (app/lib/operately_web/mcp/tools/tasks/list_task_statuses.ex + app/test/operately_web/mcp/tools/tasks/list_task_statuses_test.exs).

MCP Tool Implementation

Module contract

  • use OperatelyWeb.Mcp.Tool
  • Module: OperatelyWeb.Mcp.Tools.{Domain}.{PascalCase}
  • File: app/lib/operately_web/mcp/tools/{domain}/{snake_case}.ex
  • Public tool name: snake_case verb phrase (stable MCP API)
  • Implement definition/0 and call/2
  • No manual registry edit — OperatelyWeb.Mcp.Catalog.Registry auto-discovers compiled modules under OperatelyWeb.Mcp.Tools.*

definition/0 fields

FieldGuidance
company_mode:none, :authenticated, or :resource_derived — never accept company_id in input
required_scopes + safety_classificationRead → ["mcp:read"] + :read_only + read_annotations(); Write → ["mcp:write"] + :write + write_annotations()
sort_orderCatalog ordering band — see reference.md
input_schema / output_schemaJsonSchema.object/2; string keys; required array only for truly required args
examplesAt least one realistic example with encoded IDs like "task_123"
discovery_metadata%{"category" => "tasks"} etc.

call/2 adaptation

  • Arguments arrive as string-key maps; API inputs use atom keys
  • Always decode IDs via OperatelyWeb.Mcp.Helpers.decode_id/1 / decode_optional_id/1 — never raw UUID parsing
  • Return {:ok, atom_key_map} matching output_schema top-level keys, or {:error, :invalid_arguments | :not_found | :forbidden | …}
  • Use {:error, :invalid_arguments} for bad client input (malformed IDs, conflicting scopes, invalid enums)
  • Optional field conventions are domain-specific — check sibling tools:
    • Omit optional field to clear (e.g. update_project_due_date: omit due_date → clear)
    • Use Helpers.parse_day_date/1, markdown_to_rich_text/1, put_optional/3 as appropriate
  • Enums: validate against a whitelist before String.to_existing_atom/1, or use explicit decode clauses (see close_project, create_link)
  • Hub scope: exactly one of space_id, project_id, goal_id via Helpers.decode_hub_scope/1; prefer Api.Wrappers.DocsAndFiles.* for create/list operations

Do not call business logic directly from MCP — always go through API.

Testing

Two test layers — both required for new capabilities:

LayerFile patternFrameworkWhat to test
API (exhaustive)app/test/operately_web/api/{domain}/{action}_test.exsOperatelyWeb.TurboCaseAuth, validation, permissions, happy paths
MCP (adaptation)app/test/operately_web/mcp/tools/{domain}/{action}_test.exsOperately.DataCase + ToolConnHelperCall ToolModule.call/2 directly; ID decoding; cross-company not_found; optional-field behavior

MCP tests assume API correctness — do not re-test every permission matrix in MCP tests.

Catalog test (always update): add name to @expected_tool_names in app/test/operately/mcp/tools_test.exs. Missing entry fails CI.

Run targeted tests:

make test FILE=app/test/operately_web/api/tasks/list_task_statuses_test.exs
make test FILE=app/test/operately_web/mcp/tools/tasks/list_task_statuses_test.exs
make test FILE=app/test/operately/mcp/tools_test.exs

Optional integration: app/test/operately_web/controllers/mcp_controller_test.exs for end-to-end HTTP — only when adding novel transport/auth behavior.

Checklist

  • Confirmed no existing MCP tool covers the capability
  • API handler exists (or was added) and registered in api.ex
  • API tests pass (TurboCase)
  • MCP wrapper under OperatelyWeb.Mcp.Tools.* with correct schemas, scopes, company_mode, sort_order
  • MCP unit tests pass (call ToolModule.call/2 via ToolConnHelper)
  • @expected_tool_names updated in tools_test.exs
  • Catalog test passes
  • Optional fields / clear-on-omit behavior matches sibling tools in the domain

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Clean-code engineering standards for writing, refactoring, and reviewing code in any programming language. Use this whenever the user asks to write clean code, follow clean-code principles, refactor for clarity, improve naming, reduce complexity or duplication, separate concerns, tighten error handling, work test-first or do TDD, or otherwise raise code quality, readability, and maintainability. Also use when writing or reviewing Operately APIs or Ecto queries. Apply these rules by default when producing or changing code for a quality-conscious user.

日本語の概要は準備中です。原文の説明を表示しています。

operately/operately5792026年10月10日 更新

Defines where UI components belong in Operately (TurboUI-first). Use when creating, changing, reviewing, or migrating UI components, adding features that need UI, or deciding whether to refactor legacy app UI in app/assets/js/components or app/assets/js/features. Covers pure TurboUI components, component reuse, the app bridge pattern, and legacy migration scenarios.

日本語の概要は準備中です。原文の説明を表示しています。

operately/operately5792026年10月10日 更新

Rules for Operately schema migrations (app/priv/repo/migrations/) and data migrations (app/lib/operately/data/change_*.ex). Use when adding, renaming, reviewing, or generating database migrations, ecto.gen.migration, Operately.Data.Change* modules, backfills, schema_migrations version collisions, mix ecto.migrate, or make gen.migration.

日本語の概要は準備中です。原文の説明を表示しています。

operately/operately5792026年10月10日 更新

help-docs

無料

Discover help documentation work from operately git history. Use when the user asks to audit what needs documenting since a release, tag, or SHA, or to identify documentation gaps from code changes. Requires a baseline SHA or tag as input.

日本語の概要は準備中です。原文の説明を表示しています。

operately/operately5792026年10月10日 更新

Maintain Operately translations when adding or changing user-visible copy, fixing missing translations, or adding a supported language. Covers shared Gettext/i18next catalogs, glossaries, generation, and completeness checks; excludes translating user-authored content or general prose outside the product.

日本語の概要は準備中です。原文の説明を表示しています。

operately/operately5792026年10月10日 更新

Operately frontend data fetching with TanStack Query. Use when adding or changing page loaders, model hooks, Api.* calls, mutations, useLoadedData, Pages.useRefresh, or any web UI backend request. New code must use TanStack. When fixing or extending an existing surface, migrate that surface's API calls to TanStack in the same change.

日本語の概要は準備中です。原文の説明を表示しています。

operately/operately5792026年10月10日 更新

operately のスキルをすべて見る

このスキルの問題を報告する