Skip to content

PROTOCOL_SPEC: Add module ID constraint, naming conventions, and standard constants #2

@tercel

Description

@tercel

Summary

Real-world integrations (tiptap-apcore, nestjs-apcore, django-apcore) revealed several gaps in PROTOCOL_SPEC.md that caused cross-implementation incompatibilities. This issue tracks the protocol-level additions needed.

Issues Addressed

1. Module ID Format Constraint (P0 — from tiptap-apcore ISSUE-002)

Problem: The spec did not restrict characters in module_id. The MCP/OpenAI normalizer converts dots to hyphens (image.resizeimage-resize), but module IDs containing hyphens (e.g., my-app.toggle-bold) cannot be round-tripped. The normalization is lossy.

Fix: Add a regex constraint to the spec:

^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$

Hyphens are explicitly prohibited — reserved for dot→hyphen normalization (must be bijective).

2. Cross-Language Naming Conventions (P0 — from tiptap-apcore mega-finding)

Problem: No cross-language binding rules defined. apcore-typescript uses camelCase (moduleId, getDefinition()) while apcore-mcp-typescript used snake_case (module_id, get_definition()). The two packages were incompatible without a shim.

Fix: Add a naming convention mapping table:

Protocol (canonical) TypeScript Python
module_id moduleId module_id
input_schema inputSchema input_schema
call_async() callAsync() call_async()
get_definition() getDefinition() get_definition()

3. Standard Registry Event Names (P0 — from tiptap-apcore ISSUE-001, django-apcore Django-2.5)

Problem: No standard event names defined. Each implementation used different names ("register", "module:registered", etc.), making RegistryListener and cross-implementation interoperability impossible.

Fix: Define two standard events with callback signatures:

Event Triggered Callback
"register" After module registered (module_id, module) -> None
"unregister" Before module removed (module_id, module) -> None

All SDKs MUST export as named constants (REGISTRY_EVENTS.REGISTER / REGISTRY_EVENTS["REGISTER"]).

4. Error Code Constants Export Requirement (P1 — from tiptap-apcore ISSUE-003)

Problem: Error codes were magic strings. ErrorMapper checks error.code === "ACL_DENIED" via hardcoded strings with no exported constants.

Fix: All SDKs MUST export an ErrorCodes constant with all framework error codes from Section 7.

5. ContextFactory Protocol (P0 — from django-apcore Django-2.1)

Problem: No standard adapter pattern for creating Context from HTTP requests. Every framework integration (Django, Flask, NestJS) invented its own approach.

Fix: Define ContextFactory protocol: create_context(request) -> Context.

Affected Sections

  • Section 3 (Module ID definition) — lines 195, 201-220
  • New section: Cross-Language Naming Conventions — lines 3834-3848
  • New section: Standard Registry Event Names — lines 3850-3859
  • New section: Error Code Constants Export Requirement — lines 3861-3872
  • New section: Context Factory Protocol — lines 3874-3885

Source

  • apcore-mcp-typescript/docs/issues/tiptap-apcore-findings.md (ISSUE-001, ISSUE-002, ISSUE-003, naming mega-finding)
  • django-apcore/docs/django-apcore/upstream-sdk-analysis.md (Django-2.1, Django-2.5)
  • nestjs-apcore/docs/upstream-improvements.md (event mechanism, context passing)

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions