
بروتوكول نقل موحد يركز على الأمان للوصول إلى الأدوات وتنسيق الوكلاء. يلغي UAP ثغرات CVE-2025-49596 وتسميم أدوات MCP باستخدام بطاقات القدرة الموقعة بـ Ed25519، و mTLS الإلزامي، ومصادقة Keycloak IdP، وعزل Docker المؤقت لكل استدعاء.
██╗ ██╗ █████╗ ██████╗
██║ ██║██╔══██╗██╔══██╗
██║ ██║███████║██████╔╝
██║ ██║██╔══██║██╔═══╝
╚██████╔╝██║ ██║██║
╚═════╝ ╚═╝ ╚═╝╚═╝
Universal Agent Protocol
بروتوكول سلكي موحد يركز على الأمان للوصول إلى الأدوات، وتنسيق الوكيل، و RPC المهيكل. مبني لسد CVE-2025-49596 وفئة ثغرات تسميم الأدوات والهروب من الصندوق الرملي التي تركها MCP مفتوحة هيكلياً.
git clone https://github.com/RajSidwadkar/UAP-protocol
cd UAP-protocol
npm install
npx tsx scripts/generate-keypair.ts
docker compose -f docker-compose.dev.yml up -d
curl http://localhost:3000/health
# {"status":"ok","version":"1.0.0"}
يؤدي ذلك إلى تشغيل Keycloak على المنفذ :8080 وبوابة UAP على المنفذ :3000. تفرض البوابة mTLS، وتتحقق من بطاقات القدرة (CapabilityCards) الموقعة بـ Ed25519، وتنفذ كل استدعاء أداة في حاوية Docker مؤقتة. لا حاجة لأي إعدادات تتجاوز زوج المفاتيح المُولَّد.
graph TD
A[Client / Agent SDK] -->|mTLS + JWT + card_sig| B[UAP Gateway<br/>Fastify · port 3000]
B --> C{8-Stage Pipeline}
C --> C1[1 · Frame parse<br/>UapMessageFactory]
C1 --> C2[2 · Schema validate<br/>AJV against schema_ref]
C2 --> C3[3 · Token verify<br/>Keycloak JWKS · max 15 min]
C3 --> C4[4 · Scope enforce<br/>PermissionEnforcer]
C4 --> C5[5 · Card verify<br/>Ed25519 signature check]
C5 --> C6[6 · Sandbox execute<br/>Docker · CapDrop ALL · 128 MB]
C6 --> C7[7 · Audit emit<br/>AuditEventBus · non-blocking]
C7 --> C8[8 · Response<br/>UapResponseEnvelope]
B --> R[(Service Registry<br/>InMemory · Redis)]
B --> KC[(Keycloak IdP<br/>OAuth 2.1 · PKCE)]
B --> OT[(OpenTelemetry<br/>W3C TraceContext)]
B --> AU[(Audit Log<br/>append-only NDJSON)]
طبقات سداسية (Hexagonal layers) — النطاق (Domain) ليس له أي تبعيات I/O. كل شاغل بنية تحتية يقع خلف واجهة منفذ مصنفة. استبدل Docker بـ WASM، Keycloak بـ Auth0، Pino بمُصدِّر SIEM — لا تأثير على النطاق.
CVE-2025-49596 (تسميم الأدوات عبر بيانات وصفية غير موقعة). أوصاف أدوات MCP قابلة للتغيير بعد النشر. يمكن للمهاجم حقن تعليمات ضارة في أسماء الأدوات أو أوصافها بعد النشر — لا يملك العميل طريقة لاكتشاف التلاعب. يضع UAP كل البيانات الوصفية للأداة داخل حمولة CapabilityCard الموقعة بـ Ed25519. أي تغيير بعد التوقيع يُبطل التوقيع وترفض البوابة البطاقة قبل تنفيذ أي شيء.
CWE-284 / فئة الهروب من الصندوق الرملي. يشغل MCP الأدوات على مستوى عملية الخادم. أي اجتياز للمسار (path traversal) أو حقن عملية (process injection) في أي أداة يصل إلى نظام الملفات المضيف والشبكة. ينشئ UAP حاوية Docker جديدة لكل استدعاء — rootfs للقراءة فقط، CapDrop: ALL، حد ذاكرة 128 ميجابايت، الشبكة معطلة افتراضيًا. تُدمر الحاوية بعد الرد. لا يوجد سطح هجوم دائم بين الاستدعاءات.
CWE-287 / مصادقة الوكيل المشوش (Confused-deputy auth). يعمل MCP كموفر OAuth خاص به، مما يجعله خادم الموارد وخادم التفويض في آن واحد. هذا هو النمط الكلاسيكي للوكيل المشوش. يفصل UAP بين هذه الأدوار: البوابة هي خادم موارد بحت. Keycloak (أو أي IdP خارجي) هو السلطة الوحيدة. يتم التحقق من JWT مقابل نقطة نهاية JWKS عن بُعد وبسقف زمني صارم قدره 15 دقيقة.
كل رسالة UAP تستخدم نفس الغلاف. يحمل كتلة auth JWT مقيد النطاق ومرجعًا لبطاقة قدرة الوكيل الموقعة. تتحقق البوابة من كليهما قبل أن يصل الطلب إلى أي كود تطبيق.
{
"uap": {
"version": "1.0",
"type": "tool_call",
"id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
"trace": {
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
},
"auth": {
"token": "eyJhbGciOiJSUzI1NiJ9...",
"scope": ["tool:read"],
"card_sig": "ed25519:a1b2c3d4..."
}
},
"method": "tools/invoke",
"schema_ref": "uap:tool.invoke/v1",
"params": {
"tool_id": "db:query",
"input": { "sql": "SELECT 1" }
},
"ack": true
}
UAP-protocol/
├── packages/
│ ├── gateway/ @uap/gateway — Fastify-based UAP Gateway
│ ├── sdk-ts/ @uap/sdk-ts — TypeScript client + server SDK
│ └── sdk-py/ uap-sdk — Python async SDK (httpx + FastAPI)
├── scripts/
│ ├── generate-keypair.ts — Ed25519 keypair for local dev
│ └── keycloak-bootstrap.ts — Idempotent Keycloak realm setup
└── docker/
└── sandbox/ — Base image for zero-trust tool execution
import { UapClient, ClientCredentialsTokenProvider } from "@uap/sdk-ts";
const client = new UapClient({
gatewayUrl: "https://gateway.example.com",
tokenProvider: new ClientCredentialsTokenProvider({
tokenUrl: process.env.TOKEN_URL!,
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
}),
});
const result = await client.invokeTool(
"db:query",
{ sql: "SELECT * FROM users LIMIT 10" },
["tool:read"]
);
const task = await client.delegateTask(
"summarizer",
{ text: "..." },
["task:submit"]
);
from uap_sdk.client import UapClient, UapClientOptions
from uap_sdk.token import ClientCredentialsTokenProvider
async with UapClient(UapClientOptions(
gateway_url="https://gateway.example.com",
token_provider=ClientCredentialsTokenProvider(
token_url=os.environ["TOKEN_URL"],
client_id=os.environ["CLIENT_ID"],
client_secret=os.environ["CLIENT_SECRET"],
)
)) as client:
result = await client.invoke_tool("db:query", {"sql": "SELECT 1"}, ["tool:read"])
وسيطة FastAPI
from uap_sdk.middleware import uap_auth
@app.post("/summarize")
@uap_auth(scope=["task:submit"])
async def summarize(request: Request):
claims = request.state.uap_claims # typed AuthClaims
...
يقوم واجهة CLI uap-migrate بتغليف أي خادم MCP كبطاقة قدرة UAP موقعة في أقل من 5 دقائق. عملية محايدة (Idempotent) — إعادة التشغيل على خادم تم ترحيله بالفعل لا تؤدي إلى أي تأثير.
node dist/cli/migrate.js mcp http://localhost:3001 \
--issuer did:uap:my-org \
--key config/dev.privkey.hex \
--out ./uap-cards
# → ./uap-cards/did:uap:my-org.card.json
أسماء الأدوات، أوصافها، ومخططات المعاملات هي جزء من الحمولة الموقعة بـ Ed25519. أي تغيير لأي حقل بعد التوقيع يُبطل التوقيع — مما يهزم تسميم الأدوات هيكليًا.
const signed = await signer.sign({
issuer: "did:uap:my-agent",
version: "1.0.0",
tools: [{
id: "db:query",
description: "Run a read-only SQL query",
inputSchema: { type: "object", required: ["sql"] },
scopes: ["tool:read"]
}],
scopes: ["tool:read"],
issuedAt: Date.now(),
expiresAt: Date.now() + 86_400_000 * 30
});
// signed.signature === "ed25519:a1b2c3..."
تسمية الفروع: feat/<sprint>-<task>-<slug> — مثلاً feat/s2-2.1-mtls-keycloak
كل تغيير يتبع: فرع → npx tsc --noEmit + npx vitest run محلياً → commit → PR → دمج سحق (squash merge) → حذف الفرع. لا اعتماد على CI للتطوير المحلي.
# تشغيل جميع الاختبارات قبل الالتزام
cd packages/gateway && npx vitest run
cd packages/sdk-ts && npx vitest run
cd packages/sdk-py && python -m pytest tests/ -v
UAP · Universal Agent Protocol · 2026
SPDX-License-Identifier: Apache-2.0
| الطبقة | المحتويات | التبعيات |
|---|
| Domain | UapEnvelope, CapabilityCard, AuditEvent, Task | لا شيء — stdlib فقط |
| Application | InvokeToolUseCase, DelegateTaskUseCase, ValidateCardUseCase | منافذ Domain فقط |
| Infrastructure | KeycloakAuthAdapter, DockerSandboxAdapter, Ed25519SignerAdapter, PinoAuditAdapter | منافذ App + مكتبات خارجية |
| Transport | محلل إطارات UAP-RPC، بوابة Fastify، معالجات SSE/WebSocket | منافذ البنية التحتية + مسلسلات النطاق |
| الميزة | UAP | MCP | A2A | JSON-RPC 2.0 |
|---|
| المصادقة الإلزامية | ✅ mTLS + JWT دائمًا | ❌ اختياري | ⚠️ مفتاح API فقط | ❌ لا شيء |
| تكامل بيانات الأداة | ✅ حمولة موقعة بـ Ed25519 | ❌ إمكانية التغيير بعد النشر | ❌ لا توقيع | ❌ لا توقيع |
| نموذج الصندوق الرملي | ✅ حاوية Docker مؤقتة لكل استدعاء | ⚠️ على مستوى الخادم فقط | ❌ لا شيء | ❌ لا شيء |
| اكتشاف الوكيل | ✅ مركز-وتفرع (Hub-and-spoke) · اتصالات N | ❌ N² HTTP مباشر | ⚠️ قائم على DNS | ❌ لا شيء |
| مسار التدقيق (Audit trail) | ✅ سجل ملحق فقط أصلي للبروتوكول | ❌ لا شيء | ❌ لا شيء | ❌ لا شيء |
| التتبع الموزع (Distributed tracing) | ✅ W3C TraceContext إلزامي | ❌ لا شيء | ❌ لا شيء | ❌ لا شيء |
| التحقق من المخطط (Schema validation) | ✅ AJV في طبقة النقل | ⚠️ Zod اختياري | ❌ لا شيء | ❌ لا شيء |
| الحد الأقصى لعمر التوكن | ✅ 15 دقيقة مفروضة | ❌ غير مفروض | ❌ غير مفروض | ❌ غير قابل للتطبيق |
| دلالات الدفعة (Batch semantics) | ✅ تسلسلي · متوازي · تعامدي | ❌ غير محدد | ❌ لا شيء | ⚠️ غامض |
| الحمولات الثنائية (Binary payloads) | ✅ إطارات أصلية | ❌ Base64 فقط | ❌ Base64 فقط | ❌ Base64 فقط |
| نموذج موفر الهوية (IdP) | ✅ IdP خارجي فقط | ❌ الخادم هو موفر OAuth الخاص به | ⚠️ يختلف | ❌ لا شيء |
| محولات الجسر (Bridge adapters) | ✅ جسور MCP + A2A في المرحلة الثانية | ❌ لا جسر | ❌ لا جسر | ❌ لا جسر |
| التوسع الأفقي (Horizontal scale) | ✅ بدون حالة · أي موازن تحميل | ❌ جلسات ثابتة | ⚠️ يختلف | ❌ لا شيء |
| المتغير | الوصف | الافتراضي |
|---|
KEYCLOAK_URL | عنوان Keycloak الأساسي | http://localhost:8080 |
KEYCLOAK_REALM | اسم النطاق (Realm) | uap |
UAP_AUDIENCE | مطالبة الجمهور (audience claim) لـ JWT | uap-gateway |
UAP_SIGNING_KEY_PATH | مسار المفتاح الخاص Ed25519 بالنظام الست عشري | config/dev.privkey.hex |
UAP_DOCKER_SOCKET | مسار مأخذ Docker | /var/run/docker.sock |
UAP_AUDIT_LOG_DIR | دليل سجل التدقيق NDJSON الملحق فقط | /var/log/uap |
UAP_GATEWAY_PORT | منفذ استماع البوابة | 3000 |
UAP_MTLS_CERT | شهادة TLS للخادم (PEM) | certs/server.crt |
UAP_MTLS_KEY | مفتاح TLS الخاص للخادم (PEM) | certs/server.key |
UAP_MTLS_CA | شهادة CA للتحقق من شهادة العميل | certs/ca.crt |
OTEL_EXPORTER_OTLP_ENDPOINT | نقطة نهاية جامع OpenTelemetry | http://localhost:4318 |
REDIS_URL | Redis لسجل الخدمات متعدد الحالات (اختياري) | — |
| الحزمة | الإصدار | الغرض |
|---|
fastify | ^4.27 | خادم HTTP للبوابة |
zod | ^3.23 | مخطط الغلاف واستنتاج الأنواع |
@noble/curves | ^1.4 | توقيع Ed25519 |
jose | ^5.4 | التحقق من JWT، عميل JWKS |
ajv | ^8.16 | التحقق من المعاملات في طبقة النقل |
dockerode | ^4.0 | محول الصندوق الرملي الموثوق به |
pino | ^9.2 | سجل تدقيق مهيكل ملحق فقط |
ulid | ^2.3 | معرفات فريدة قابلة للترتيب |
@opentelemetry/sdk-node | ^0.52 | التتبع الموزع |
ioredis | ^5.3 | سجل خدمات متعدد الحالات |
@modelcontextprotocol/sdk | ^1.0 | محول جسر MCP |
httpx (Python) | ^0.27 | عميل HTTP غير متزامن لـ Python SDK |
pydantic (Python) | ^2.7 | التحقق من الأنواع في Python |