Motivation
Provide a minimal, low-overhead way for clients connected to the same AHP server to publish services and exchange application-defined notifications and requests:
Client A -> AHP server -> service registered by Client B
For example, a small process running beside remote Git repositories could connect as an AHP client and expose getRefs and batched getObjects requests to another client. Results can be cached by immutable object identity. One-way events use notifications. This requires neither an agent turn nor a separate listening port.
The abstraction is a client service, not a per-method notification handler. One registration supports multiple application-defined methods. The AHP server routes messages without understanding Git or other application protocols.
Required properties
- Notifications are strictly fire-and-forget. No queue-acceptance response, delivery acknowledgement, processing acknowledgement, or automatic per-message return traffic.
- Services are observable. Clients obtain the current visible services and receive changes without polling.
- Requests are supported as a first-class, separate path. Ordinary request/response routing must not add acknowledgement, correlation, or pending-request overhead to notifications.
- No model invocation, tool lifecycle, or chat/session mutation is required to exchange messages.
1. Registration
Names and placement are illustrative; existing AHP envelope/channel conventions would still apply.
type ClientService = {
serviceId: string; // Opaque, server-generated routing identity.
clientId: string; // Owner, established by the server.
metadata: Record<string, JsonValue>;
};
// Client -> server request: client/registerService
// params:
{ metadata: Record<string, JsonValue> }
// result:
{ serviceId: string }
// Client -> server request: client/unregisterService
// params:
{ serviceId: string }
// result:
{}
Example metadata:
{
"protocol": "git-object-reader",
"version": 1,
"label": "Remote Git"
}
Metadata is descriptive, not an authorization mechanism. The application protocol defines method names, parameters, and results; methods do not need individual registration or server-side schemas.
Lifecycle
- Registrations belong to the connection that created them; multiple services per connection are allowed.
- Only the owning connection may unregister a service.
- Disconnect removes that connection's services.
- Reconnect requires registering again with new service IDs. Old IDs never redirect to replacement services.
- Unregistering rejects new requests and fails outstanding requests for that service. Notifications targeting a removed service are not forwarded.
- No persistent registrations, leases, offline delivery, or replay.
2. Observable service discovery
Expose a client-service catalog through the existing AHP subscribe/state mechanism:
type ClientServicesState = {
services: ClientService[];
};
- Subscription supplies an initial snapshot followed by changes, without a list/watch race.
- Registration, unregistration, disconnect cleanup, and visibility changes update the catalog. If metadata updates are supported, they must also be observable.
- Each observer sees only services visible to it.
- Reconnecting observers obtain a fresh snapshot.
- Discovery updates are not message-delivery acknowledgements. Observing a service does not guarantee it remains connected when a later message is sent.
The concrete channel URI and action names should follow AHP conventions. A separate polling-only listServices API is not necessary if subscription already supplies the current snapshot. No durable discovery history is required.
3. Notifications: strictly no acknowledgement
// Client -> server JSON-RPC NOTIFICATION: clientService/notify
// params:
{
serviceId: string;
method: string;
params: JsonValue;
}
// Server -> service owner JSON-RPC NOTIFICATION: clientService/notification
// params:
{
serviceId: string;
senderClientId: string; // Server-stamped, not supplied by the sender.
method: string;
params: JsonValue;
}
Neither message has a JSON-RPC id. Neither produces a result or error response.
- No queue-acceptance acknowledgement, recipient acknowledgement, processing acknowledgement, or per-message error notification.
- No relay-maintained pending-request entry or response correlation.
- Forward each notification at most once; no automatic retries or deduplication of separate sends.
- Preserve server receive order for notifications actually forwarded from one connection to one service. No ordering guarantee across senders.
- Disconnect can lose in-flight notifications.
- Unknown/stale service or unauthorized sends are not forwarded, with no per-message reply.
- Local validation can fail before sending. Underlying transport backpressure and transport failures still exist; "no acknowledgement" describes this relay protocol, not its underlying transport.
Resource limits must not introduce acknowledgement traffic. A bounded overload policy, such as dropping messages or closing an offending connection, needs specification.
4. Requests: first-class request/response
// Client -> server JSON-RPC REQUEST: clientService/request
// params:
{
serviceId: string;
method: string;
params: JsonValue;
}
// result:
JsonValue
// Server -> service owner JSON-RPC REQUEST: clientService/handleRequest
// params:
{
serviceId: string;
senderClientId: string; // Server-stamped.
method: string;
params: JsonValue;
}
// result:
JsonValue
Example:
Explorer AHP server Git sidecar
| | |
|-- request id=7 ------------>| |
| serviceId=git-42 |-- handleRequest id=93 ------>|
| method=getObjects | |
| params={repo, oids} |<----- result id=93 ----------|
|<----- result id=7 ----------| |
The server maps connection-local request IDs and routes the result or JSON-RPC error back to the original caller. No application-level reply service, replyTo, or correlation ID is necessary.
The application result/error is the response. There is no separate queue-acceptance acknowledgement.
- Unknown service, unauthorized access, malformed requests, and exhausted request limits return explicit errors.
- Service method failures return JSON-RPC errors; application error codes/data should be preserved within the defined bounds.
- Service disconnect or unregistration fails affected pending requests. Caller disconnect releases the caller's relay bookkeeping.
- Requests have bounded lifetimes and bounded in-flight counts. The exact timeout policy needs specification.
- Each original request completes at most once. Late responses after timeout, cancellation, or disconnect cannot complete a newer request.
- Reuse the existing AHP cancellation mechanism where applicable and specify forwarding across the mapped request IDs rather than inventing a second mechanism.
- Timeout/cancellation/disconnect does not prove the remote operation did not execute. Do not automatically retry requests.
Request bookkeeping is allocated only for requests, never for notifications.
Semantics at a glance
| Situation |
Notification |
Request |
| Unknown service / unauthorized |
Not forwarded; no reply |
Error |
| Service disconnects |
May be lost; no reply |
Pending request fails |
| Service method fails |
No automatic reply |
JSON-RPC error |
| Timeout |
No tracking |
Bounded timeout |
| Success |
Nothing returned |
Application result |
Notifications and requests share discovery and authorization, not delivery guarantees or reply overhead.
Authorization and limits
- Tie service ownership and sender identity to the server-established client identity; a caller-supplied client ID alone is not an authentication boundary.
- Apply authorization to discovery, notifications, and requests. Knowing a service ID does not authorize invocation.
- A host may allow communication within an existing shared trust boundary, but the protocol must not assume all connected clients are mutually trusted.
- Bound metadata, method names, parameters, results/error data, queued bytes/messages, registrations, and in-flight requests.
- Control-plane and service requests may return errors. Notification delivery must not generate per-message error replies or diagnostics sent back as an acknowledgement channel.
- Do not log application payloads by default.
Why not client tools or resource reads?
Client-provided tools support publication and execution results, but their documented invocation lifecycle is tied to agent/chat tool calls. Client services enable direct programmatic calls independent of turns and transcript state.
Bidirectional resource reads can support narrow lookups, but a filesystem-shaped bridge is not a general service API. This proposal avoids encoding arbitrary messages as resource paths.
Expected overhead
- Notification: one service-table lookup and one forwarded notification, with zero relay response traffic and no request-correlation state.
- Request: a routed request and returned result/error, with request-ID mapping but no extra acceptance acknowledgement.
- Only service catalog changes produce discovery updates; application traffic is not broadcast to unrelated clients or stored in chat/session state.
Performance should be measured during implementation; this is not a benchmark claim.
Open questions
- Which AHP channel URI and state actions should represent the observable service catalog?
- How should service visibility and invocation authorization integrate with the existing host trust model?
- Which bounded overload/drop/disconnect policy should apply to notifications without per-message return traffic?
- What request timeout, cancellation-forwarding, and relay-error conventions best fit AHP?
- What naming, envelope placement, and unsupported-host behavior best fit existing AHP conventions?
Both requests and notifications are in scope. Whether notifications require acknowledgement is not an open question: they must not.
Suggested acceptance tests
- Two clients register/discover services and exchange notifications and requests without creating a session or invoking a model.
- A service handles multiple methods under one registration, including batched Git object lookup.
- A notification wire trace shows only send and forwarded delivery: no acceptance response, delivery acknowledgement, error reply, or automatic return traffic.
- A request wire trace shows forwarded request and returned result/error, with no additional acceptance response.
- Services are observable through an initial snapshot plus registration/unregistration/disconnect/visibility changes, without polling or snapshot/subscription races.
- Stale service IDs never route to replacements; registration ownership and discovery/invocation authorization are enforced.
- Request correlation remains correct with simultaneous callers using identical local request IDs; only the expected recipient can complete a forwarded request.
- Disconnect, unregistration, timeout, cancellation, late responses, and request-limit failures clean up bookkeeping and cannot complete a request twice.
- Notification ordering and bounded resource use hold under load; request support adds no per-notification acknowledgement or pending-request state.
Motivation
Provide a minimal, low-overhead way for clients connected to the same AHP server to publish services and exchange application-defined notifications and requests:
For example, a small process running beside remote Git repositories could connect as an AHP client and expose
getRefsand batchedgetObjectsrequests to another client. Results can be cached by immutable object identity. One-way events use notifications. This requires neither an agent turn nor a separate listening port.The abstraction is a client service, not a per-method notification handler. One registration supports multiple application-defined methods. The AHP server routes messages without understanding Git or other application protocols.
Required properties
1. Registration
Names and placement are illustrative; existing AHP envelope/channel conventions would still apply.
Example metadata:
{ "protocol": "git-object-reader", "version": 1, "label": "Remote Git" }Metadata is descriptive, not an authorization mechanism. The application protocol defines method names, parameters, and results; methods do not need individual registration or server-side schemas.
Lifecycle
2. Observable service discovery
Expose a client-service catalog through the existing AHP subscribe/state mechanism:
The concrete channel URI and action names should follow AHP conventions. A separate polling-only
listServicesAPI is not necessary if subscription already supplies the current snapshot. No durable discovery history is required.3. Notifications: strictly no acknowledgement
Neither message has a JSON-RPC
id. Neither produces a result or error response.Resource limits must not introduce acknowledgement traffic. A bounded overload policy, such as dropping messages or closing an offending connection, needs specification.
4. Requests: first-class request/response
Example:
The server maps connection-local request IDs and routes the result or JSON-RPC error back to the original caller. No application-level reply service,
replyTo, or correlation ID is necessary.The application result/error is the response. There is no separate queue-acceptance acknowledgement.
Request bookkeeping is allocated only for requests, never for notifications.
Semantics at a glance
Notifications and requests share discovery and authorization, not delivery guarantees or reply overhead.
Authorization and limits
Why not client tools or resource reads?
Client-provided tools support publication and execution results, but their documented invocation lifecycle is tied to agent/chat tool calls. Client services enable direct programmatic calls independent of turns and transcript state.
Bidirectional resource reads can support narrow lookups, but a filesystem-shaped bridge is not a general service API. This proposal avoids encoding arbitrary messages as resource paths.
Expected overhead
Performance should be measured during implementation; this is not a benchmark claim.
Open questions
Both requests and notifications are in scope. Whether notifications require acknowledgement is not an open question: they must not.
Suggested acceptance tests