Skip to content

feat: add plugin attribution via operation_context (Phase 2)#56

Open
arorashivam96 wants to merge 6 commits into
mainfrom
feat/operation-context
Open

feat: add plugin attribution via operation_context (Phase 2)#56
arorashivam96 wants to merge 6 commits into
mainfrom
feat/operation-context

Conversation

@arorashivam96
Copy link
Copy Markdown
Contributor

@arorashivam96 arorashivam96 commented May 9, 2026

Summary

Add plugin attribution to all outbound Dataverse requests via User-Agent tagging. Every SDK call, raw Web API call, and MCP proxy request now carries a closed-schema operation_context identifying the plugin version, skill, and agent — enabling server-side MAU/MAT, skill split, and agent distribution dashboards from existing Dataverse logs.

What changed

scripts/auth.py — Two new helpers centralize attribution:

  • get_client(skill) — returns a DataverseClient with OperationContext baked into the UA
  • get_plugin_headers(skill, token) — returns headers dict for raw Web API calls with UA attribution
  • Both validate against closed allowlists (_ALLOWED_SKILLS, _ALLOWED_AGENTS) and a strict regex. Free-form text, PII, emails, and unknown keys are rejected with ValueError.

Skill Setup blocks — All 4 SDK-using skills (dv-data, dv-query, dv-metadata, dv-solution) migrated from the 4-line DataverseClient(...) pattern to get_client("<skill>"). Each block includes an inline comment: "Do not modify the context value — it is a closed schema for server-side telemetry. Never include secrets or PII."

Level 3 references — multi-table-fk-import.md, sample-data-generation.md, alternate-keys.md also migrated.

dv-connect Step 3 — Writes DATAVERSE_PLUGIN_VERSION and DATAVERSE_PLUGIN_AGENT to .env (version hardcoded, agent auto-detected from CLAUDECODE/VSCODE_PID/CURSOR_TRACE_DIR env vars).

dv-connect Step 6 — Adds DATAVERSE_OPERATION_CONTEXT guidance for MCP server registration env block.

dv-overview — Updated routing hint from get_credential to get_client.

CLAUDE.md — Updated auth pattern section to list get_client as the preferred import alongside existing get_credential and get_token.

Version — 1.4.5 -> 1.5.0 (MINOR: new backward-compatible capabilities).

Resulting User-Agent per channel

Channel UA
Python SDK DataverseSvcPythonClient:0.1.0b10 (app=dataverse-skills/1.5.0;skill=dv-data;agent=claude-code)
CLI MCP proxy DataverseCli/1.0.0 (app=dataverse-skills/1.5.0;agent=claude-code)
Raw Web API Python-urllib (app=dataverse-skills/1.5.0;skill=dv-metadata;agent=claude-code)

Depends on

Test plan

  • python .github/evals/static_checks.py — passes (only pre-existing dv-overview token budget warning)
  • End-to-end validation: MCP create/query/delete + SDK bulk insert/query/update — all 7 operations passed (test report)
  • Agent correctly uses get_client with correct skill names and attribution comments in generated scripts
  • No regression: get_credential and get_token still work for advanced/raw-API cases

🤖 Generated with Claude Code

Add get_client() and get_plugin_headers() helpers to auth.py that build
a single operation_context string (app/skill/agent) and pass it to the
Python SDK, which appends it to the User-Agent header on all outbound
requests.

Changes:
- auth.py: new helpers with closed-schema allowlists for skill and agent
- dv-data, dv-query, dv-metadata, dv-solution: migrate Setup blocks to
  get_client("<skill>") — simpler 2-line pattern
- dv-connect: write DATAVERSE_PLUGIN_VERSION and DATAVERSE_PLUGIN_AGENT
  to .env; include DATAVERSE_OPERATION_CONTEXT in MCP registration env
- Version bump: 1.4.5 -> 1.5.0 (MINOR — new capabilities)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@arorashivam96 arorashivam96 requested a review from a team May 9, 2026 01:15
arorashivam96 and others added 3 commits May 13, 2026 10:04
…comments

Update auth.py to use the SDK's OperationContext class (not plain string)
and the renamed `context` kwarg on DataverseClient. Add regex validation
in auth.py matching the SDK's own format check as a defense-in-depth
layer.

Add inline comments to all skill code blocks warning not to modify the
context value and not to include secrets or PII. The context uses a
closed schema (app/skill/agent from allowlists) — free-form text,
emails, and special characters are rejected by both auth.py and the SDK.

Also migrates remaining Level 3 reference files (multi-table-fk-import,
sample-data-generation, alternate-keys) from get_credential to
get_client for consistency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add get_client as the preferred auth pattern in CLAUDE.md skill
  authoring rules, alongside existing get_credential and get_token
- Fix dv-connect Step 3: add missing plugin_version = "1.5.0" in the
  .env writer code block (was referenced but never defined)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The hardcoded plugin_version in dv-connect Step 3 is now a 5th place
the version lives. Update:
- static_checks.py: EVAL-VERSION-01/02 now check dv-connect SKILL.md
  plugin_version alongside the 4 JSON manifest fields
- CLAUDE.md: Version Bumping section lists all 5 locations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread .github/plugins/dataverse/skills/dv-connect/SKILL.md Outdated
```python
import os

plugin_version = "1.5.0"
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.

anyway, at least in these scripts to extract version from json files? rather than one more set of hard coded versions?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  • dv-connect Step 3 now reads version from .claude-plugin/plugin.json (with marketplace.json as fallback), eliminating the hardcoded plugin_version = "1.5.0"
  • Reverted CLAUDE.md back to 4 version locations (the 5th no longer exists)
  • Reverted static_checks.py — removed the _extract_dv_connect_plugin_version helper and the 5th check

5. Register the MCP server (Copilot: write JSON config; Claude: run `claude mcp add` command)
6. Handle admin consent and environment allowlist (one-time per tenant/environment)

**Plugin attribution for MCP:** When registering the stdio MCP server, include `DATAVERSE_OPERATION_CONTEXT` in the env block so the CLI appends it to its User-Agent on all outbound requests. Build the value from `.env`:
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.

can we confirm if we are using actual mcp server using orgurl/mcp http mcp server or a local stdio mcp server?
based on that this statement might change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  • Step 6 now explicitly states "stdio proxy" transport (npx @microsoft/dataverse mcp ) and explains that DATAVERSE_OPERATION_CONTEXT works because the CLI runs as a local subprocess that reads env
    vars at startup

arorashivam96 and others added 2 commits May 15, 2026 08:58
Address PR review feedback:
- dv-connect Step 3: read plugin_version from .claude-plugin/plugin.json
  instead of hardcoding it. Falls back to marketplace.json, then "unknown".
  Eliminates the 5th version location that required manual sync.
- dv-connect Step 6: clarify that the plugin uses stdio proxy transport
  (npx @microsoft/dataverse mcp <url>), not direct HTTP-streamable MCP.
  Add note about X-Dataverse-Plugin header for future HTTP-streamable
  agents.
- Revert CLAUDE.md and static_checks.py back to 4 version locations
  since the hardcoded version no longer exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the X-Dataverse-Plugin header note from dv-connect Step 6.
Dataverse does not log custom headers in telemetry, so this approach
is not viable for HTTP-streamable MCP attribution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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