Skip to content

refactor(core): move the resource schema into arcade-core [TOO-1931] - #923

Open
EricGustin wants to merge 1 commit into
mainfrom
ericgustin/too-1931-resource-schema-to-arcade-core
Open

refactor(core): move the resource schema into arcade-core [TOO-1931]#923
EricGustin wants to merge 1 commit into
mainfrom
ericgustin/too-1931-resource-schema-to-arcade-core

Conversation

@EricGustin

@EricGustin EricGustin commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

arcade-serve needs the resource shapes so it can serve them, and it cannot import arcade-mcp-server, because the dependency runs the other way. This moves the result-shaped half of the resource models down into arcade-core and re-exports them from arcade_mcp_server.types, so both libraries read one definition instead of drifting apart.

Part 1 of 6 in a stack. Replaces #917, which GitHub auto-closed when I renamed the head branch. Same commit, same content.

Resolves: https://linear.app/arcadedev/issue/TOO-1931

Design decisions

The models land in arcade_core/resource_schema.py, parallel to the existing arcade_core/schema.py that holds the tool schema. Moved: Result, PaginatedResult, BaseMetadata, Icon, Annotations, Cursor, Role, Resource, ResourceTemplate, ResourceContents, TextResourceContents, BlobResourceContents, ListResourcesResult, ListResourceTemplatesResult, ReadResourceResult, ReadResourceParams.

The three request envelopes stay where they are. ListResourcesRequest and its siblings extend JSONRPCRequest, so moving them would drag JSONRPCMessage, Request and RequestId into arcade-core for no gain. A transport that carries no JSON-RPC envelope only ever needs the params object.

ListResourcesParams is new. arcade-mcp-server never modelled it, since PaginatedRequest.params is a bare dict[str, Any].

A serialization bug came along with the move

Resource, ResourceTemplate and ResourceContents all declare meta with alias="_meta", but their model config never set populate_by_name. Combine that with extra="allow" and Resource(meta=...) gets quietly accepted as an extra field, then serialized under the wrong key:

>>> Resource(uri="u", name="n", meta={"ui": {"x": 1}}).model_dump(by_alias=True, exclude_none=True)
{"name": "n", "uri": "u", "meta": {"ui": {"x": 1}}}     # and .meta reads None

Nothing reads meta, so anything a developer put in _meta would have disappeared without an error at any layer. Result already set populate_by_name, which is why the classes behaved differently and why nobody caught it. Fixed here with a regression test, since these are the classes being moved.

Scope

In scope: relocating the models, adding the missing params object, keeping arcade_mcp_server.types importable exactly as before.

Not in scope: anything that reads or serves these models. That starts in #918.

Test plan

  • uv run pytest libs/tests: 3807 passed, 1 skipped
  • uv run mypy . clean in each of arcade-core, arcade-serve, arcade-tdk, arcade-mcp-server
  • ruff check and ruff format --check clean on every touched file
  • 10 new tests in libs/tests/core/test_resource_schema.py covering the field names, the _meta alias in both directions, and the text-versus-blob distinction
  • One re-export test asserting the moved names are the same objects under both import paths, which is what catches a later edit redefining a model instead of re-exporting it

Author checklist

Before moving this PR from Draft to Ready for Review:

  • Linked to a Linear ticket or GitHub issue (above)
  • I understand every change in the diff
  • Runs locally, exercised through the end-user path (not just unit tests)
  • make check and make test are green locally; CI is expected to pass
  • I've pulled the branch fresh and reviewed my own diff top-to-bottom
  • I'd merge it myself if a teammate said LGTM right now

Note

Medium Risk
Touches shared wire-contract Pydantic models and serialization behavior for _meta; backward compatible for import paths but any consumer depending on the old silent meta extra could see different output.

Overview
Moves MCP resource list/read result models from arcade_mcp_server.types into new arcade_core.resource_schema, so arcade-serve can share one wire definition without depending on arcade-mcp-server. arcade_mcp_server.types re-exports the same classes (identity asserted in tests) so existing imports keep working; JSON-RPC request types like ListResourcesRequest stay in the MCP package.

Adds ListResourcesParams (optional pagination cursor) for non–JSON-RPC callers. While relocating models, fixes _meta handling: populate_by_name on BaseMetadata and ResourceContents so meta= maps to the _meta wire key instead of being swallowed as an extra field under meta.

Bumps arcade-core to 4.12.0 and arcade-mcp-server to 1.26.1 with the updated core floor. New tests lock wire serialization (_meta, text vs blob, MIME strings) and re-export parity.

Reviewed by Cursor Bugbot for commit 5944cbf. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread libs/tests/arcade_mcp_server/test_types_reexport.py Outdated
@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch from 1d296c5 to d3eb3a2 Compare August 26, 2026 20:32
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
libs/arcade-core/arcade_core/resource_schema.py 100.00% <100.00%> (ø)
libs/arcade-mcp-server/arcade_mcp_server/types.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@EricGustin
EricGustin marked this pull request as ready for review August 26, 2026 21:20
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown

Greptile Summary

The PR moves shared MCP resource schemas into arcade-core, re-exports them from arcade_mcp_server.types, and corrects _meta population behavior.

  • Adds shared resource, pagination, and read-result models to arcade_core.resource_schema.
  • Preserves existing MCP-server import paths through identity-preserving re-exports.
  • Updates package versions and the MCP server’s minimum arcade-core dependency.
  • Adds serialization, validation, content-union, and re-export identity tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
libs/arcade-core/arcade_core/resource_schema.py Introduces the centralized resource wire models and fixes name-based population of aliased _meta fields.
libs/arcade-mcp-server/arcade_mcp_server/types.py Replaces local resource model definitions with backward-compatible re-exports while retaining JSON-RPC request envelopes.
libs/arcade-core/pyproject.toml Applies the required minor version increase for the new core schema module.
libs/arcade-mcp-server/pyproject.toml Applies a patch version increase and raises the core dependency floor to the version containing the imported module.
libs/tests/core/test_resource_schema.py Covers resource wire shapes, aliased metadata, pagination defaults, validation, and text/blob distinctions.
libs/tests/arcade_mcp_server/test_types_reexport.py Ensures legacy and core import paths resolve to the same schema objects.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Serve[arcade-serve] --> Core[arcade_core.resource_schema]
  MCP[arcade_mcp_server.types] -->|re-exports| Core
  Consumers[Existing MCP consumers] --> MCP
Loading

Reviews (3): Last reviewed commit: "refactor(core): move the resource schema..." | Re-trigger Greptile

@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch 2 times, most recently from 77fee3a to b8871e0 Compare August 27, 2026 00:15
arcade-serve needs the worker format's resource shapes to serve them, and it
cannot import arcade-mcp-server because the dependency runs the other way. Move
the result-shaped half of the resource surface into arcade-core and re-export it
from arcade_mcp_server.types, so both libraries read one definition.

The request envelopes stay behind. ListResourcesRequest and its siblings extend
JSONRPCRequest, which would pull JSONRPCMessage, Request and RequestId into
arcade-core to no purpose: a transport that does not carry a JSON-RPC envelope
needs the params object alone. ListResourcesParams is added for that params
object, which arcade-mcp-server never modelled (PaginatedRequest.params is a
bare dict).

Also fixes a live serialization bug in the models being moved. Resource,
ResourceTemplate and ResourceContents declare meta with alias="_meta", but their
config never set populate_by_name, so Resource(meta=...) was accepted as an
extra field and emitted as "meta". No caller reads that key, so a
developer-declared _meta.ui.csp would have vanished with no error at any layer.
Result already had populate_by_name, which is why the two behaved differently.
@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch from b8871e0 to 5944cbf Compare August 27, 2026 15:28

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

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.

1 participant