Skip to content

1.9.79: update and delete tools for traffic sources, their accounts and categories in the MCP connector - #280

Merged
tracking202 merged 6 commits into
1.9.79from
1.9.79-setup-objects-crud
Oct 27, 2026
Merged

tracking202 merged 6 commits into
1.9.79from
1.9.79-setup-objects-crud

Conversation

@tracking202

Copy link
Copy Markdown
Owner

Backlog item mcp-setup-objects-crud.

The MCP connector (p202 mcp and the hosted server) could only list and create traffic sources (ppc_network), traffic source accounts (ppc_account) and categories (aff_network). So when asked to rename "Google Ads - Main" to "Main" it could only make a second source, and an account's default flag could not be changed after it was created. This PR adds:

  • ppc_network_update, ppc_network_delete
  • ppc_account_update, ppc_account_delete
  • aff_network_update, aff_network_delete

These are the same kind of tool as landing_page_update/_delete. Each runs the p202 <entity> update|delete command, and those commands, like PUT/DELETE /api/v3/{ppc-networks,ppc-accounts,aff-networks}/{id}, already existed. So nothing changes in the API or in the CLI commands.

What happens on a delete

  • The delete follows the existing convention: a soft delete (the *_deleted flag), run through the tool with --force once the call is approved, and dry_run: true gives a preview first (deleteNote).
  • Children are not touched. This matches Setup › Traffic Sources / Setup › Categories (ppc_accounts.php, aff_networks.php set the flag on that one row) and the API's Controller::deleteRecord(). A campaign delete likewise leaves its trackers. A deleted source's accounts and a deleted category's campaigns stay and still name their parent. The dry run's cascade is []. I did not add a cascade or a refusal, because either would make the API disagree with the Setup pages. Instead, the delete tools' descriptions say what stays and name the tool that removes it (ppc_account_delete, campaign_delete). The OpenAPI DELETE descriptions and the CLI docs say the same.

Account scoping (IDOR)

  • Writes are scoped by the base controller: get()/update()/deleteRecord() filter on user_id, and assertLinksOwned checks ppc_network_id on an account update.
  • New tests/Api/V3/SetupObjectsWriteScopeIntegrationTest:
    • Another account's source, account or category answers 404 for an update, a delete and a dry run, and its row stays byte-for-byte the same.
    • A rename keeps the id and the row count, and the children still name the parent.
    • A delete soft-deletes that one row only.
  • Hosted scope: AiConnect::WRITE_AREAS already holds ppc-networks, ppc-accounts and aff-networks. Existing connections need no reconnect, and neither AiConnect.php nor connect-ai.php changes. Its copy already says "change and delete ... traffic sources and categories".

Also in this PR

  • p202 search tasks for the three update commands.
  • Agent eval setup-source-001: rename in place, no create or delete. Reference agent branch included.
  • Docs: documentation/cli/10-go-cli.md, docs/cli.md, docs/cli-agent.md, docs/openapi.yaml.
  • Changelogs: changelogs.txt and 202-config/changelogs.json.

Overlap with #260, #265, #267

Verification (local, macOS, PHP 8.5, MySQL 8.0 in Docker)

  • cd go-cli && go vet ./cmd/ && go test ./cmd/ passed. The TestMCPToolArgv cases are new. The hosted-scope test runs every tool in each mode against AiConnect's areas.
  • SetupObjectsWriteScopeIntegrationTest: 3 tests, 84 assertions, OK. It is not vacuous: with the user_id scope removed from PpcNetworksController, the cross-account test fails.
  • OwnedReferencesIntegrationTest, PayloadValidationIntegrationTest and IntegrationSuiteSelectionTest passed.
  • verify.sh --changed --since origin/1.9.79:
    go PASS, golangci PASS, openapi PASS, patterns PASS, phpcs PASS, syntax PASS
    phpstan FAIL  phpstan.neon.dist passed, phpstan-strict.neon failed
    unit FAIL     local PHP is 8.5, CI pins 8.3
    
    The phpstan-strict findings are in Connection.php and LookupRepositoryFactory.php, which this PR does not touch. The unit tier's failures are the same 14 test files with identical counts on origin/1.9.79 (checked with these changes stashed), so they come from PHP 8.5, not from this change.
  • Not run locally: a live instance or the eval case end to end. The Agent Evals job on this PR is the first run of setup-source-001.

tracking202 and others added 5 commits October 27, 2026 02:15
…categories

ppc_network_update/_delete, ppc_account_update/_delete and
aff_network_update/_delete run p202 ppc-network, ppc-account and
aff-network update and delete, which already existed. The connector could
only list and create these, so a rename made a second source and an
account's default flag could not be changed.

A delete removes the one record (soft delete), as Setup › Traffic Sources
and Setup › Categories do; the delete tools' descriptions say that a
source's accounts and a category's campaigns stay, and name the tool that
removes them. The hosted connector needs no new scope: AiConnect's
WRITE_AREAS already covers ppc-networks, ppc-accounts and aff-networks.

The catalogue budget goes to 85,000 characters for the six tools (about
4,600).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…category

Search tasks for ppc-network, ppc-account and aff-network update, with the
words a rename is asked in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ecords

SetupObjectsWriteScopeIntegrationTest: an update renames the record in
place (same id, no new row, children still name it); another account's
source, account or category answers 404 for an update, a delete and a dry
run and is left as it was; a delete soft-deletes that one row, leaves the
source's accounts and the category's campaigns, and its dry run reports
mode soft with an empty cascade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setup-source-001 asks for "EVAL Source - Main" to be renamed and checks one
source under the new name, none under the old, and its account still under
it; creating or deleting a source or account fails it. The reference agent
runs p202 ppc-network update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MCP section names the new tools; cli.md, cli-agent.md and the OpenAPI
DELETE descriptions say a delete removes the source, account or category
alone and what stays. Changelog entry for both changelogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 27, 2026 09:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T09:36:31.897711Z 62d30b1 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

Code review: CLEAN

For da4d5df: 0 issue(s). The review is encrypted for the maintainers in the review-findings artifact of this run.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da4d5dfce2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread go-cli/cmd/mcp_catalog.go
{Name: "ppc_network_delete", Command: []string{"ppc-network", "delete"}, Title: "Delete a traffic source", Kind: mcpDelete, Args: []mcpArg{idArg("id", "Traffic source id (from ppc_network_list); or give ids for several.", false)}, Note: setupParentDeleteNote("traffic source", "accounts", "ppc_account_delete")},
{Name: "ppc_account_list", Command: []string{"ppc-account", "list"}, Title: "List traffic source accounts", Kind: mcpRead, HostedOmit: mcpEveryPage},
{Name: "ppc_account_create", Command: []string{"ppc-account", "create"}, Title: "Create a traffic source account", Kind: mcpCreate},
{Name: "ppc_account_update", Command: []string{"ppc-account", "update"}, Title: "Update a traffic source account", Kind: mcpUpdate, Args: []mcpArg{idArg("id", "Traffic source account id (from ppc_account_list).", true)}},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict none to report filters

The new ppc_account_update tool inherits mcpIDFlagPattern for its ppc_network_id flag, whose schema accepts none even though that exception is intended only for report filters. Thus an MCP client can submit a schema-valid request such as {"id":"9","ppc_network_id":"none"}, but the PPC account controller requires an integer and rejects the update with a 422. Generate the numeric-only pattern for this update field, or make the none exception depend on the command being a report.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 62d30b1: mcpIDFlagPattern now takes the tool kind and offers none for ppc_network_id only on read tools (the report filters). On write tools (ppc_account_create, ppc_account_update) the field is digits only, and {"ppc_network_id":"none"} is refused before any request. TestMCPToolArgv pins it: none is refused on ppc_account_update and ppc_account_create, and report_summary still takes none. The two refusal cases fail against the previous mcp_catalog.go. go test ./cmd/ passes.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Adversarial review: NON-BLOCKING

For 62d30b1: 3 finding(s). The report is encrypted for the maintainers in the review-findings artifact of this run.

Rewritten on every push; the check fails on BLOCKING.

mcpIDFlagPattern gave every ppc_network_id the report filters' pattern,
digits or none, so ppc_account_create and ppc_account_update advertised and
sent none, which the API refuses. none is now a read tool's only; the argv
tests pin none refused on both writes and still taken by report_summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tracking202
tracking202 merged commit 1591da1 into 1.9.79 Oct 27, 2026
20 checks passed
tracking202 pushed a commit that referenced this pull request Oct 28, 2026
#280's traffic source and category update and delete tools sit beside
the text ad writes in the catalogue; text_ad_list takes #279's hosted
omission of all. The catalogue budget goes to 88,000 characters: #280's
tools and the text ad writes together come to about 87,800. The docs
keep #281's hosted read bounds, then the text ad reconnect note; the
changelogs keep 1.9.79's lines and put the text ad lines after them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
tracking202 pushed a commit that referenced this pull request Oct 28, 2026
…start IPN branch

GET /users/{id}/preferences now serves every integration secret as its
stored state (#270), and the PayKickstart IPN keys are one more:
paykickstart_ipn_secret_keys is on ServedPreferences::SECRET, answered
{"stored": true}, and the count this branch served in its place
(paykickstart_ipn_keys_stored, PayKickstartKeys::withoutKeys()) is gone.
GET /system/integrations keys_stored still says how many. The
PreferenceSecretsAreNotServedIntegrationTest round trip stores the keys
with the other secrets.

Conflicts: changelogs (1.9.79's lines, then this branch's), the
preferences docs and the OpenAPI schemas (1.9.79's, with the keys added
as a StoredSecret), and the controller (1.9.79's).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
tracking202 pushed a commit that referenced this pull request Oct 28, 2026
The catalogue keeps #280's traffic source, account and category updates and
deletes beside the rotator writes, and rotator_list takes #279's hosted
omission of --all. Both changelogs keep the version branch's lines and add
this branch's. The tool list is 89,469 characters with both; its budget is
90,000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
tracking202 pushed a commit that referenced this pull request Oct 28, 2026
"set the api url" ranked p202 system integrations level with config
set-url once the rotator writes and #280's tasks were both in the index:
the two had scored within half a percent on the version branch, and the new
commands' words moved the weights enough to tie them. config set-url now
has a search task with the words the docs use for what it sets, the
install's base URL or instance URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
tracking202 pushed a commit that referenced this pull request Oct 28, 2026
…JVZoo branch

#268 now dates a notification's row by the provider's own time, held to the
click for a sale and to the sale for a refund, and brings 1.9.79's #270
(ServedPreferences: GET preferences serves each secret as {"stored": ...}),
#280, #281 and #282.

Conflicts: both changelogs keep the base's lines with the JVZoo line after
them; the OpenAPI preferences update takes #270's oneOf shape, with the
JVZoo key's rule in its string branch; the eval cases keep #268's admin-005
notes and add admin-006 after it. jvzoo_ipn_secret_key is already on
ServedPreferences::SECRET, and JVZoo adds no other column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr
tracking202 pushed a commit that referenced this pull request Oct 28, 2026
…79's #270, #280, #281, #282) into the ClickBank branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRoS2SViuxaucFaKWsFnLr

# Conflicts:
#	202-config/changelogs.json
#	changelogs.txt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants