Instruction file imported from Scopeo/draftnrun (
.cursor/rules/mcp.mdc). Copyright stays with the author.
MCP Server Conventions
- Standalone process — no imports from
ada_backend/orengine/. - Tools are thin wrappers: validate inputs, call
DraftnrunClientorSupabaseClient, format response. No business logic. - Tool naming:
snake_caseverbs matching the API action (list_,get_,create_,update_,delete_). - Auth:
SupabaseProviderfrom FastMCP. Tokens are standard Supabase JWTs. MCP server does NOT issue its own tokens. - Consent page: lives in
back-office/src/pages/oauth/callback.vue, NOT in the MCP server. Supabase redirects toSITE_URL + authorization_pathfor consent. - Use
get_access_token()fromfastmcp.server.dependenciesto get the JWT;.tokenfor the raw string,.claims["sub"]for user ID. - Org context: session-scoped via Redis and keyed by the MCP session ID when available. Tools must check for active org before calling org-scoped endpoints (use
require_org_context). Developer+ operations (create/update/delete/pause/resume cron, OAuth,create_agent, knowledge mutations, Typeform webhook setup, git sync configure/disconnect) userequire_role.trigger_cronis org-scoped (member+). - Workflow creation:
create_workflowcreates workflow-type projects. Agent projects usecreate_agent. - Target-org permissions: tools that accept an explicit
org_idmust validate the caller's role on that target organization, not only on the active org session. - Component search:
search_components(query)must reject blank or whitespace-only queries instead of returning the full catalog. - Error handling: never bare except. Map HTTP errors to descriptive MCP tool error messages via
ToolError. - Sentry:
MCPIntegrationis auto-configured. Usesentry_sdk.loggerfor structured logging. - Response trimming: large payloads (>50KB) are truncated by
DraftnrunClient._trim_response. Trimming is controllable per-tool viaToolSpec.trim(defaultTrue) and per-call via thetrimparameter on all HTTP methods.get_graphandlist_componentsare untrimmed. - When adding/removing/renaming MCP tools: update
mcp_server/README.md, the relevantdocs://resource inmcp_server/docs.py, and this rule file in the same diff. - When changing any MCP behavior, payload, auth, limitation, or guardrail: update the matching
docs://resource inmcp_server/docs.pyin the same diff. - Example UUIDs in
docs.pymust come from code (uuid.uuid4()), stored as module constants / placeholders replaced at import — never hand-written hex in prose. See_GRAPH_DOC_*indocs.py. - When changing file/document behavior: always update
docs://file-managementandmcp_server/README.mdin the same diff. - When adding a new tool domain: create a file in
mcp_server/tools/, register inmcp_server/tools/__init__.py. - Never reuse IDs, instance UUIDs, source IDs, or graph JSON from another project/org as a shortcut. Re-fetch current state with
get_graph,list_components,search_components,list_sources, etc. - Ask for explicit user permission before publish/delete/revoke OAuth or guiding OAuth setup in the web UI.
- Prefer dedicated components over
python_code_runner/terminal_command_runner; keep code tools for small, bounded tasks. - Prefer dedicated search components for web research when the catalog already provides them.
- Do not describe knowledge documents as original downloadable files or claim MCP has a generic file upload/download API unless that behavior actually exists.
- Factory spec validation at registration time catches unresolved path placeholders,
body_org_keywithout org/role scope, and missing roles on role-scoped specs. - Body fields with
Nonevalues are omitted from the JSON payload (not serialized as JSONnull). - Redis fallback is recoverable: after a connection failure, the client retries automatically after 60 s.
- Treat
_truncatedresponses as partial data, never as complete truth. get_org_token_usagereports monthly input/output tokens from stored trace spans only;years/monthsaccept lists or"all", and per-model rows depend on persistedmodel_idvalues, not recomputation from prompts or provider APIs.- Tool port configurations: component instances in graph payloads include
port_configurations(setup modes:ai_filled,user_set,deactivated) and a computedtool_description.tool_description_overridecustomizes the description shown to the AI. When changing tool port behavior, updatedocs://graphs,docs://agent-config, andmcp_server/README.md. - QA custom columns: entry
custom_columnsdicts are keyed by column UUID, not display name. Always uselist_custom_columnsto discover the mapping before writing. Seedocs://qaanddocs://known-quirks. - CSV tools:
export_dataset_csv/import_dataset_csvhandle dataset round-trips. The export builds CSV from paginatedlist_entriesinternally (nograph_runner_idneeded). The import uses multipart file upload to the backend's CSV import endpoint. - Client methods:
api.get_raw()returns raw text (for non-JSON endpoints).api.post_file()handles multipart uploads. Both are available for custom tools that need non-JSON request/response patterns. - Org selection must be sequential — parallel calls with
select_organizationrace and fail. Seedocs://known-quirks. update_graphandupdate_graph_topology_v2tool descriptions warn callers toget_guide('graphs')first and to close the browser tab to avoid race conditions with the UI auto-save.update_graph_topology_v2additionally documents full-replace edge semantics.configure_agent,add_tool_to_agent, andremove_tool_from_agentare for AGENT-type projects only. For WORKFLOW-type projects, useupdate_component_parametersto change component parameters, orget_graph+update_graphfor structural changes.- Keep
get_project_overviewparity with API-level project settings exposed byget_project. - AI Agent
skip_tools_with_missing_oauth(defaultTrue): at agent startup, tools withis_available()False (e.g. missing OAuth) are excluded from the LLM tool registry. Documented indocs://agent-config,docs://integrations, andmcp_server/README.mdguardrails; change viaconfigure_agentorupdate_component_parameters. - Neverdrop-branded Google integration components use distinct OAuth provider filters:
google-mail-neverdropandgoogle-calendar-neverdrop. Always discover the component viasearch_components()and verify matching OAuth connections withlist_oauth_connections(provider_config_key)before graph edits. update_component_parametersperforms a read-modify-write on a single component's parameters. It GETs the full graph to locate the component, then PUTs only the target component via the V2 single-component endpoint (/v2/.../components/{instance_id}), avoiding the V1 full-graph hash-based skip. Preferred overupdate_graphfor single-parameter changes on workflow components. Avoid modifyingdrives_output_schemafields (payload_schema,output_format) unless intending to change dynamic output ports. Internally filters out INPUT-kind parameters and converts read-formatfield_expressionsto write-formatinput_port_instancesbefore PUT to prevent lossy round-trips (e.g.json_buildexpressions whosevalueis the non-invertible placeholder[JSON_BUILD]). RaisesToolErrorif the caller tries to set a literal value on a parameter wired with a non-literal field expression (reforjson_build); useupdate_component_v2to rewire those parameters.- Run retries are run-scoped via
retry_run(project_id, run_id, env=None). The backend reuses the original run'sgraph_runner_idand persistedrun_inputs. The optionalenvis a legacy fallback only needed when bothgraph_runner_idandenvare null on the original run; do not reintroduce project-scoped retry policy fields. update_graphsupports optional optimistic locking: passlast_edited_timefrom a previous response to detect concurrent edits (409 Conflict).- File-based graph v2 tools: granular tools (
create_component_v2,update_component_v2,delete_component_v2,update_graph_topology_v2) for front/MCP. Git sync uses the same service functions internally (no MCP tool). Granular flow: create component → update topology to connect it. Field expression refs ininput_port_instancessupportfile_key(e.g.{"type": "ref", "file_key": "start", "port": "output"}) as an alternative toinstanceUUID; the v2 mapper resolvesfile_key→ UUID before saving.update_component_v2uses full-replace semantics forparametersandinput_port_instances— callers must include ALL parameters from the graph, not only the ones being changed. For single-parameter changes preferupdate_component_parameters. - Edge
origin/destinationaccept both plain UUID strings and dicts like{"instance_id": "uuid"}— the backend normalizes. - JSON-typed parameters (e.g. If/Else
conditions) accept both native lists and JSON-encoded strings — the backend normalizes lists viaparse_expression_flexible. - If/Else branch edges use
order: 0for true andorder: 1for false/else. Setenable_false_path=trueon the component before wiringorder: 1; when it is false, false conditions select no downstream branch.