You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: Add module ID constraints, naming conventions, and SDK export requirements to protocol spec
Add three new subsections to PROTOCOL_SPEC.md to resolve ambiguities that
caused downstream integration issues in tiptap-apcore, nestjs-apcore, and
django-apcore:
1. Module ID Format Constraint (Section 2.1) — explicit regex, prohibited
characters (hyphens reserved for MCP normalization), and validation
requirement during register()
2. Cross-Language Naming Conventions (Section 11.2) — canonical snake_case
to native convention mapping table for TS/Python/Go/Rust, with bridge
package convention rule
3. Registry Event Names, Error Code Constants Export, and Context Factory
Protocol (Section 11.2) — standardized event names with named constants,
mandatory error code enum exports, and ContextFactory for web framework
integrations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: PROTOCOL_SPEC.md
+74Lines changed: 74 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -198,6 +198,27 @@ directory_to_id:
198
198
max_length: 128
199
199
```
200
200
201
+
#### Module ID Format Constraint
202
+
203
+
All module IDs **MUST** conform to the following regular expression:
204
+
205
+
```
206
+
^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$
207
+
```
208
+
209
+
**Allowed characters:**
210
+
- Lowercase ASCII letters (`a-z`)
211
+
- Digits (`0-9`)
212
+
- Underscores (`_`)
213
+
- Dots (`.`) as namespace separators only
214
+
215
+
**Explicitly prohibited:**
216
+
- Hyphens (`-`) — reserved for MCP/OpenAI tool name normalization (dot→hyphen conversion must be bijective)
217
+
- Uppercase letters
218
+
- Spaces, special characters
219
+
220
+
All SDK implementations **MUST** validate module IDs against this pattern during `register()`. Invalid IDs **MUST** be rejected with `GENERAL_INVALID_INPUT` error.
221
+
201
222
### 2.2 ID Map (Cross-language Conversion)
202
223
203
224
**ID Map** module handles cross-language ID conversion, supporting automatic recognition and manual configuration. Implementations **must** support canonical conversion from various language native formats to Canonical ID.
**Rule:** Bridge/adapter packages (e.g., apcore-mcp-typescript) **MUST** use the same naming conventions as their language's core SDK. A TypeScript MCP bridge **MUST** use camelCase to match apcore-typescript, not snake_case from the protocol spec.
3849
+
3850
+
#### Standard Registry Event Names
3851
+
3852
+
Registry implementations **MUST** support exactly two standard events:
3853
+
3854
+
| Event Name | Triggered | Callback Signature |
3855
+
|-----------|-----------|-------------------|
3856
+
|`"register"`| After module successfully registered |`(module_id, module) -> None`|
3857
+
|`"unregister"`| Before module is removed |`(module_id, module) -> None`|
3858
+
3859
+
All SDKs **MUST** export these event names as named constants (e.g., TypeScript: `REGISTRY_EVENTS.REGISTER`, Python: `REGISTRY_EVENTS["REGISTER"]`). Consumers **MUST NOT** hardcode event name strings.
3860
+
3861
+
#### Error Code Constants Export Requirement
3862
+
3863
+
All SDKs **MUST** export the framework error codes defined in Section 7 as enumerated constants. This prevents magic string dependencies and enables IDE autocomplete.
For web framework integrations (Django, Flask, FastAPI, NestJS, Express), SDKs **SHOULD** provide a `ContextFactory` protocol:
3877
+
3878
+
```
3879
+
Protocol ContextFactory:
3880
+
create_context(request: Any) -> Context
3881
+
```
3882
+
3883
+
This enables framework-specific context creation (e.g., extracting Identity from Django `request.user`, JWT tokens, or API keys) without coupling apcore core to any web framework.
3884
+
3885
+
The lifecycle is: request arrives → ContextFactory.create_context(request) → Executor.call(module_id, inputs, context) → response.
0 commit comments