Skip to content

Commit 9cd7332

Browse files
tercelclaude
andcommitted
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>
1 parent c525fe8 commit 9cd7332

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

PROTOCOL_SPEC.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,27 @@ directory_to_id:
198198
max_length: 128
199199
```
200200
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+
201222
### 2.2 ID Map (Cross-language Conversion)
202223

203224
**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.
@@ -3810,6 +3831,59 @@ Interface: MetricsCollector
38103831
observe(name: String, labels: Map, value: Float) → void
38113832
```
38123833

3834+
#### Cross-Language Naming Conventions
3835+
3836+
The protocol specification uses `snake_case` for canonical definitions. Each language SDK **MUST** translate to its native naming convention:
3837+
3838+
| Protocol (canonical) | TypeScript | Python | Go | Rust |
3839+
|---------------------|------------|--------|-----|------|
3840+
| `module_id` | `moduleId` | `module_id` | `ModuleId` | `module_id` |
3841+
| `input_schema` | `inputSchema` | `input_schema` | `InputSchema` | `input_schema` |
3842+
| `output_schema` | `outputSchema` | `output_schema` | `OutputSchema` | `output_schema` |
3843+
| `get_definition()` | `getDefinition()` | `get_definition()` | `GetDefinition()` | `get_definition()` |
3844+
| `call_async()` | `callAsync()` | `call_async()` | `CallAsync()` | `call_async()` |
3845+
| `requires_approval` | `requiresApproval` | `requires_approval` | `RequiresApproval` | `requires_approval` |
3846+
| `open_world` | `openWorld` | `open_world` | `OpenWorld` | `open_world` |
3847+
3848+
**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.
3864+
3865+
Example (TypeScript):
3866+
```typescript
3867+
export const ErrorCodes = {
3868+
MODULE_NOT_FOUND: "MODULE_NOT_FOUND",
3869+
SCHEMA_VALIDATION_ERROR: "SCHEMA_VALIDATION_ERROR",
3870+
// ... all codes from Section 7
3871+
} as const;
3872+
```
3873+
3874+
#### Context Factory Protocol
3875+
3876+
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.
3886+
38133887
### 11.3 Cross-language Implementation Requirements
38143888

38153889
| Requirement | Python | Rust | Go | Java | TypeScript |

0 commit comments

Comments
 (0)