From 37870924165724ccf0a7213638ed74c34bb7f778 Mon Sep 17 00:00:00 2001 From: Jerome Louvel <374450+jlouvel@users.noreply.github.com> Date: Fri, 6 Mar 2026 19:46:12 -0500 Subject: [PATCH 1/2] Integrated wiki and blueprints markdown for easier management --- README.md | 8 +- .../agent-skills-support-proporal.md | 1582 ++++++++++++ ...gent-skills-support-schema-amendments.json | 653 +++++ .../blueprints/gap-analysis-report.md | 923 +++++++ .../mcp-resources-prompts-proposal.md | 1330 +++++++++++ src/main/resources/wiki/Contribute.md | 11 + src/main/resources/wiki/FAQ,md | 822 +++++++ src/main/resources/wiki/Installation.md | 131 + src/main/resources/wiki/Releases.md | 7 + src/main/resources/wiki/Roadmap.md | 52 + src/main/resources/wiki/Specification-v0.4.md | 2112 +++++++++++++++++ src/main/resources/wiki/Specification.md | 2112 +++++++++++++++++ src/main/resources/wiki/Tutorial.md | 353 +++ src/main/resources/wiki/Use-cases.md | 122 + 14 files changed, 10214 insertions(+), 4 deletions(-) create mode 100644 src/main/resources/blueprints/agent-skills-support-proporal.md create mode 100644 src/main/resources/blueprints/agent-skills-support-schema-amendments.json create mode 100644 src/main/resources/blueprints/gap-analysis-report.md create mode 100644 src/main/resources/blueprints/mcp-resources-prompts-proposal.md create mode 100644 src/main/resources/wiki/Contribute.md create mode 100644 src/main/resources/wiki/FAQ,md create mode 100644 src/main/resources/wiki/Installation.md create mode 100644 src/main/resources/wiki/Releases.md create mode 100644 src/main/resources/wiki/Roadmap.md create mode 100644 src/main/resources/wiki/Specification-v0.4.md create mode 100644 src/main/resources/wiki/Specification.md create mode 100644 src/main/resources/wiki/Tutorial.md create mode 100644 src/main/resources/wiki/Use-cases.md diff --git a/README.md b/README.md index 5bd123c..615fc1d 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ # Naftiko Framework -Welcome to Naftiko Framework, the first Open Source project for **Spec-Driven AI Integration**. It reinvents API integration for the AI era with a governed and versatile platform based on capabilities that streamlines API sprawl created by the massive SaaS growth and microservices. +Welcome to Naftiko Framework, the first Open Source project for **Spec-Driven AI Integration**. It reinvents API integration for the AI era with a governed and versatile platform based on capabilities that streamlines API sprawl created by the massive SaaS and microservices growth. -Each capability is a coarse piece of domain that consumes existing HTTP-based APIs, converts into JSON data formats like Protocol Buffer, XML, YAML, CSV and Avro, enabling better Context Engineering and API reusability critical to AI integration. +Each capability is a coarse piece of domain that consumes existing HTTP-based APIs, converts data formats like Protocol Buffer, XML, YAML, CSV and Avro into JSON, enabling better Context Engineering and API reusability critical to AI integration. -Capabilities are declared using **YAML** files, configuring the Naftiko Engine provided as a **Docker** container. Clients can then consume the capability via the **MCP** or **API** servers exposed. +Capabilities are declared using **YAML** files, configuring the Naftiko Engine provided as a **Docker** container. Clients can then consume the capability via the **MCP** or **API** servers exposed. While the framework itself is developed in Java and can be extended to support new protocols, developers just need to know YAML, JSONPath and Mustache templates to take full advantage of it. -- :ship: [Use cases](https://github.com/naftiko/framework/wiki/Use-Cases) - :rowboat: [Installation](https://github.com/naftiko/framework/wiki/Installation) - :sailboat: [Tutorial](https://github.com/naftiko/framework/wiki/Tutorial) +- :ship: [Use cases](https://github.com/naftiko/framework/wiki/Use-Cases) - :anchor: [Specification](https://github.com/naftiko/framework/wiki/Specification) - :mega: [Releases](https://github.com/naftiko/framework/wiki/Releases) - :telescope: [Roadmap](https://github.com/naftiko/framework/wiki/Roadmap) diff --git a/src/main/resources/blueprints/agent-skills-support-proporal.md b/src/main/resources/blueprints/agent-skills-support-proporal.md new file mode 100644 index 0000000..a057e30 --- /dev/null +++ b/src/main/resources/blueprints/agent-skills-support-proporal.md @@ -0,0 +1,1582 @@ +# Agent Skills Specification Integration Proposal +## Skill Metadata & Catalog Adapter Architecture + +**Status**: Revised Proposal +**Date**: March 5, 2026 +**Key Concept**: Dedicated `skill` server adapter — skills declare tools derived from sibling `api` and `mcp` adapters or defined as local file instructions. AI clients invoke adjacent adapters directly for derived tools. + +--- + +## Table of Contents + +1. [Executive Summary](#executive-summary) +2. [Architecture Overview](#architecture-overview) +3. [Design Analogy](#design-analogy) +4. [Skill Definition](#skill-definition) +5. [Schema Structure](#schema-structure) +6. [Predefined REST Endpoints](#predefined-rest-endpoints) +7. [Visual Architecture](#visual-architecture) +8. [Implementation Examples](#implementation-examples) +9. [Security Considerations](#security-considerations) +10. [Derivation Validation Rules](#derivation-validation-rules) +11. [Implementation Roadmap](#implementation-roadmap) +12. [Backward Compatibility](#backward-compatibility) + +--- + +## Executive Summary + +### What This Proposes + +Introduce a **dedicated `skill` server adapter** (alongside existing `api` and `mcp` adapters) enabling Naftiko capabilities to **describe skills and declare supporting tools**: + +1. **Describe** skills with full [Agent Skills Spec](https://agentskills.io/specification) frontmatter metadata +2. **Declare tools** — each tool is either derived from a sibling `api`/`mcp` adapter operation, or defined as a local file instruction +3. **Distribute** skills through predefined GET endpoints for discovery, download, and file browsing +4. **Locate** supporting files (SKILL.md, README, schemas) via a `location` property + +Skills can be **purely descriptive** (metadata + supporting files only), declare **derived tools** (from sibling adapters), declare **instruction tools** (from local files), or mix all three. The skill adapter does not execute derived tools — AI clients invoke the sibling REST API or MCP adapters directly. + +### Why a Dedicated Adapter? + +Just as the `mcp` adapter provides protocol-specific features despite HTTP being technically possible within `api`, the `skill` adapter provides: + +- **Catalog Model**: Describe skills that declare tools from sibling adapters or local instructions, giving agents a unified discovery surface +- **Agent Skills Spec Alignment**: Full frontmatter support (name, description, license, compatibility, allowed-tools, argument-hint, invocation controls) +- **Supporting Files**: `location` property links to SKILL.md and supporting documentation accessible via REST endpoints +- **Focused Responsibility**: Skill metadata concerns separate from tool execution (which stays with `api` and `mcp` adapters) + +### Business Value + +| Benefit | Impact | Users | +|---------|--------|-------| +| **Skill Cataloging** | Describe and organize tools from sibling API/MCP adapters and local instructions into discoverable skills | Developers | +| **Agent Discovery** | Agents discover skill tools with rich metadata, then invoke sibling adapters directly or read instruction files | AI Agents | +| **No Duplication** | Derive tools from existing adapters without redefining tool logic | Architects | +| **Distribution** | Predefined REST endpoints for discovery, download, and file browsing | Organizations | +| **Enterprise Control** | Host skill catalogs internally with auth, metadata governance, and supporting docs | InfoSec Teams | + +### Key Design Decisions + +1. **Metadata-First**: Skills describe and declare tools — they do not execute them. Tool execution stays with the `api` and `mcp` adapters that own the tools. + +2. **Per-Tool Declaration**: Each skill declares its tools individually via `tools[]`. Each tool specifies its source: `from` (derived from a sibling adapter) or `instruction` (a local file). + +3. **Derived Tools**: A tool with `from` references a specific operation (api) or tool (mcp) in a sibling adapter. Each derived tool includes an `invocationRef` so agents know where to invoke the tool directly. + +4. **Instruction Tools**: A tool with `instruction` references a file relative to the skill’s `location` directory. The instruction content is served through the `/contents` endpoint. + +5. **Purely Descriptive**: A skill can also stand alone with no tools — just metadata + `location` supporting files. + +6. **Full Agent Skills Spec Frontmatter**: Every property from the [Agent Skills Spec](https://agentskills.io/specification) YAML frontmatter is declarable on `ExposedSkill` — name, description, license, compatibility, metadata, allowed-tools, argument-hint, user-invocable, disable-model-invocation. + +7. **`location` for Supporting Files**: A `file:///` URI pointing to a directory containing SKILL.md and supporting files/folders, served through the `/contents` and `/download` endpoints. + +8. **No Recursive Derivation**: Only sibling `api` or `mcp` adapters can be `from` sources — no derivation from other skill adapters. + +### Risk Assessment + +| Risk | Likelihood | Impact | Mitigation | +|------|-----------|--------|-----------| +| **Path traversal attacks** | Medium | High | Strict path validation regex, character whitelist | +| **Location URI validation** | Medium | Medium | Only `file:///` scheme; validate resolved path stays within allowed directories | +| **Schema complexity** | Low | Low | ExposedSkill adds tools[] with clear from/instruction sources — no new execution paradigms | +| **Performance (ZIP generation)** | Low | Medium | Streaming, size limits, caching | + +**Overall Risk**: **LOW** — Purely additive metadata layer; no execution complexity + +--- + +## Architecture Overview + +### Current State: Existing Server Adapters in Naftiko + +| Adapter | Purpose | Responsibility | +|---------|---------|-----------------| +| **`api`** | REST API Server | HTTP endpoints, resource operations, tool execution via REST | +| **`mcp`** | MCP Protocol Server | MCP tools, stdio/HTTP transport, tool execution via MCP protocol | +| **`skill`** (NEW) | Skill Catalog & Distribution | Agent skill metadata, tools (derived + instruction), supporting files | + +Each server adapter has: +- Distinct `type` field in `exposes` blocks +- Focused endpoint responsibility +- Clear separation of concerns + +### Proposed Architecture + +The `skill` adapter is a **metadata and catalog layer** — it describes skills that declare tools from sibling `api` and `mcp` adapters or from local file instructions: + +```yaml +capability: + consumes: + # Consumed HTTP APIs (backing operations) + - type: "http" + namespace: "weather-api" + baseUri: "https://api.weather.com/v1/" + resources: + - path: "forecast/{{location}}" + operations: + - method: "GET" + name: "get-forecast" + + - type: "http" + namespace: "geocoding-api" + baseUri: "https://geocode.example.com/" + resources: + - path: "resolve/{{query}}" + operations: + - method: "GET" + name: "resolve-location" + + exposes: + # API adapter — owns tool execution via REST + - type: "api" + address: "0.0.0.0" + port: 9090 + namespace: "weather-rest" + resources: + - path: "/forecast/{{city}}" + operations: + - method: "GET" + name: "get-forecast" + call: "weather-api.get-forecast" + with: + location: "{{city}}" + + # MCP adapter — owns tool execution via MCP protocol + - type: "mcp" + transport: "http" + address: "0.0.0.0" + port: 9091 + namespace: "weather-mcp" + tools: + - name: "resolve-and-forecast" + description: "Resolve a place name to coordinates, then fetch forecast" + steps: + - type: "call" + name: "geo" + call: "geocoding-api.resolve-location" + with: + query: "{{place}}" + - type: "call" + name: "weather" + call: "weather-api.get-forecast" + with: + location: "{{geo.coordinates.lat}},{{geo.coordinates.lon}}" + inputParameters: + - name: "place" + type: "string" + description: "Place name to resolve" + + # Skill adapter — metadata/catalog layer (no execution) + - type: "skill" + address: "0.0.0.0" + port: 8080 + namespace: "weather-skills" + + skills: + - name: "weather-forecast" + description: "Look up weather forecasts by location name or coordinates" + license: "MIT" + compatibility: "Requires network access to weather and geocoding APIs" + argument-hint: "Describe the location you want a forecast for" + location: "file:///etc/naftiko/skills/weather-forecast" + metadata: + author: "weather-team" + category: "weather" + + tools: + - name: "get-forecast" + description: "Get weather forecast for a city" + from: + namespace: "weather-rest" # Sibling API adapter + action: "get-forecast" # Operation name + - name: "resolve-and-forecast" + description: "Resolve a place name to coordinates, then fetch forecast" + from: + namespace: "weather-mcp" # Sibling MCP adapter + action: "resolve-and-forecast" # Tool name + - name: "interpret-weather" + description: "Guide for reading and interpreting weather data" + instruction: "interpret-weather.md" # Local file in location dir +``` + +**How agents use this:** +1. Agent calls `GET /skills/weather-forecast` → receives tool catalog +2. Agent sees `get-forecast` (derived) with `invocationRef: { targetNamespace: "weather-rest", mode: "api" }` → invokes `GET http://host:9090/forecast/London` +3. Agent sees `resolve-and-forecast` (derived) with `invocationRef: { targetNamespace: "weather-mcp", mode: "mcp" }` → invokes via MCP protocol on port 9091 +4. Agent sees `interpret-weather` (instruction) → reads instruction content via `GET /skills/weather-forecast/contents/interpret-weather.md` + +--- + +## Design Analogy + +``` +API Adapter MCP Adapter SKILL Adapter +───────────── ───────────── ────────────── +ExposesApi ExposesMcp ExposesSkill +├─ resources[] ├─ tools[] ├─ skills[] +│ ├─ path │ ├─ name │ ├─ name +│ ├─ inputParameters[] │ ├─ description │ ├─ description +│ └─ operations[] │ ├─ inputParameters[] │ ├─ frontmatter metadata +│ ├─ method │ ├─ call / steps │ ├─ location +│ ├─ call / steps │ ├─ with │ └─ tools[] +│ ├─ with │ └─ outputParameters[] │ ├─ name +│ ├─ inputParameters[] │ │ ├─ description +│ └─ outputParameters[] │ │ ├─ from { ns, action } + │ │ └─ instruction +``` + +| Adapter | First-class construct | Actionable units | Execution | +|---------|----------------------|-----------------|-----------| +| **`api`** | Resources | Operations | HTTP endpoints (call/steps/with) | +| **`mcp`** | (flat) | Tools | MCP protocol (call/steps/with) | +| **`skill`** | Skills | Tools (derived + instruction) | **None** — agents invoke sibling adapters or read instruction files | + +--- + +## Skill Definition + +Skills provide rich metadata and a unified discovery surface. Each skill can declare one or more **tools**, where each tool is either **derived** from a sibling `api` or `mcp` adapter, or defined as a local **instruction** file. Skills can also stand alone as purely descriptive (no tools). + +### Declaring Tools + +Each skill declares tools individually via `tools[]`. Each tool has a `name`, `description`, and exactly one source: + +- **`from`** — derives the tool from a sibling adapter operation (api) or tool (mcp) +- **`instruction`** — references a local file relative to the skill's `location` directory + +```yaml +skills: + - name: "order-management" + description: "Manage orders through the public API" + license: "Apache-2.0" + argument-hint: "Describe the order operation you need" + location: "file:///etc/naftiko/skills/order-management" + + tools: + # Derived from sibling API adapter + - name: "list-orders" + description: "List all customer orders" + from: + namespace: "public-api" + action: "list-orders" + - name: "create-order" + description: "Create a new customer order" + from: + namespace: "public-api" + action: "create-order" + # Derived from sibling MCP adapter + - name: "summarize-order" + description: "Generate an AI summary of an order" + from: + namespace: "assistant-mcp" + action: "summarize-order" + # Local file instruction + - name: "order-policies" + description: "Order processing policies and business rules" + instruction: "instructions/order-policies.md" +``` + +### Derived Tools (`from`) + +A tool with `from` references a specific operation or tool in a sibling adapter: + +**Tool declaration rules:** +1. `from.namespace` must resolve to a sibling `exposes[]` entry of type `api` or `mcp` +2. `from.action` must match an operation name (api) or tool name (mcp) in the resolved adapter +3. Adapter type is inferred from the resolved target +4. Each derived tool includes an `invocationRef` in the response so agents can invoke the source adapter directly + +**Derived tool response (returned by `GET /skills/{name}`):** +```json +{ + "name": "list-orders", + "description": "List all customer orders", + "type": "derived", + "invocationRef": { + "targetNamespace": "public-api", + "action": "list-orders", + "mode": "api" + }, + "inputSchema": { + "type": "object", + "properties": {}, + "description": "Copied from source operation input parameters" + } +} +``` + +### Instruction Tools (`instruction`) + +A tool with `instruction` references a file relative to the skill's `location` directory. The skill must have a `location` configured: + +```yaml +skills: + - name: "coding-guidelines" + description: "Company coding guidelines for AI agents" + location: "file:///etc/naftiko/skills/coding-guidelines" + + tools: + - name: "naming-conventions" + description: "Naming conventions for variables, functions, and classes" + instruction: "naming-conventions.md" + - name: "error-handling" + description: "Error handling patterns and best practices" + instruction: "error-handling.md" + - name: "testing-strategy" + description: "Unit and integration testing guidelines" + instruction: "instructions/testing-strategy.md" +``` + +**Instruction tool response (returned by `GET /skills/{name}`):** +```json +{ + "name": "naming-conventions", + "description": "Naming conventions for variables, functions, and classes", + "type": "instruction", + "instruction": "naming-conventions.md" +} +``` + +Agents read instruction content via `GET /skills/{name}/contents/{file}`. + +### Mixed Tools (Derived + Instruction) + +A single skill can mix derived and instruction tools: + +```yaml +skills: + - name: "data-intelligence" + description: "Unified data tools from REST and MCP adapters plus local instructions" + allowed-tools: "run-analysis quick-stats interpret-data" + location: "file:///etc/naftiko/skills/data-intelligence" + + tools: + - name: "run-analysis" + description: "Run a full data analysis" + from: + namespace: "analytics-rest" + action: "run-analysis" + - name: "quick-stats" + description: "Run quick statistical analysis" + from: + namespace: "analytics-mcp" + action: "quick-stats" + - name: "interpret-data" + description: "Guide for interpreting analysis results" + instruction: "instructions/interpret-data.md" +``` + +### Purely Descriptive Skills + +A skill with no tools — just metadata and supporting files: + +```yaml +skills: + - name: "onboarding-guide" + description: "New developer onboarding guide" + license: "proprietary" + location: "file:///etc/naftiko/skills/onboarding" + metadata: + author: "platform-team" + category: "onboarding" +``` + +The `location` directory contains the SKILL.md and any supporting files. These are served through the `/contents` and `/download` endpoints for distribution. + +### Agent Skills Spec Frontmatter Properties + +Every `ExposedSkill` supports the full [Agent Skills Spec](https://agentskills.io/specification) YAML frontmatter: + +| Property | Type | Required | Default | Description | +|----------|------|----------|---------|-------------| +| `name` | string | **Yes** | — | 1–64 chars, kebab-case. Skill identifier. | +| `description` | string | **Yes** | — | Max 1024 chars. What the skill does and when to use it. Used for agent discovery. | +| `license` | string | No | — | License identifier (e.g., "MIT", "Apache-2.0") | +| `compatibility` | string | No | — | Max 500 chars. Compatibility notes and requirements. | +| `metadata` | object | No | — | Arbitrary key-value pairs (author, category, tags, ecosystem, etc.) | +| `allowed-tools` | string | No | — | Space-delimited list of pre-approved tool names | +| `argument-hint` | string | No | — | Hint text shown when agents invoke via slash command | +| `user-invocable` | boolean | No | `true` | Whether agents can invoke this skill as a slash command | +| `disable-model-invocation` | boolean | No | `false` | Whether to prevent auto-loading this skill based on context | + +### The `location` Property + +The `location` property provides a `file:///` URI pointing to a directory containing SKILL.md and supporting files. The skill server serves these files through the `/contents` and `/download` endpoints: + +```yaml +skills: + - name: "weather-forecast" + description: "Weather forecasting tools" + location: "file:///etc/naftiko/skills/weather-forecast" + tools: + - name: "get-forecast" + description: "Get weather forecast" + from: + namespace: "weather-rest" + action: "get-forecast" + - name: "interpret-weather" + description: "Guide for interpreting weather data" + instruction: "interpret-weather.md" +``` + +**Expected directory structure at the location:** +``` +/etc/naftiko/skills/weather-forecast/ +├── SKILL.md # Skill documentation with frontmatter +├── README.md # Additional documentation +├── examples/ +│ ├── basic-usage.md +│ └── advanced.md +└── schemas/ + └── weather-response.json +``` + +The SKILL.md file at the location can contain the same frontmatter properties as declared on the `ExposedSkill`. The capability YAML declaration is the source of truth; the SKILL.md frontmatter is informational for file-based consumers. + +--- + +## Schema Structure + +### ExposesSkills (New Type) + +```json +{ + "type": "object", + "description": "Skill server adapter — skills declare tools derived from sibling adapters or as local file instructions. Metadata and catalog layer peer to api and mcp.", + "properties": { + "type": { + "const": "skill", + "description": "Fixed value for skill server adapter" + }, + "address": { + "$ref": "#/$defs/Address", + "description": "Listen address (0.0.0.0, localhost, hostname, etc.)" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535, + "description": "Listen port" + }, + "namespace": { + "$ref": "#/$defs/IdentifierKebab", + "description": "Unique identifier for this skill server" + }, + "description": { + "type": "string", + "description": "Description of this skill server's purpose" + }, + "authentication": { + "$ref": "#/$defs/Authentication", + "description": "Optional authentication for the skill server" + }, + "skills": { + "type": "array", + "description": "Array of skill definitions. Each skill declares tools (derived from sibling adapters or local file instructions), or stands alone as purely descriptive.", + "items": { + "$ref": "#/$defs/ExposedSkill" + }, + "minItems": 1 + } + }, + "required": ["type", "port", "namespace", "skills"], + "additionalProperties": false +} +``` + +### ExposedSkill (New Type) + +```json +{ + "type": "object", + "description": "A skill definition. Declares tools derived from sibling api or mcp adapters or defined as local file instructions. Can also stand alone as purely descriptive (no tools). Supports full Agent Skills Spec frontmatter metadata. Skills describe tools — they do not execute them.", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$", + "minLength": 1, + "maxLength": 64, + "description": "Skill identifier (kebab-case)" + }, + "description": { + "type": "string", + "maxLength": 1024, + "description": "What the skill does and when to use it. Used for agent discovery." + }, + "license": { + "type": "string", + "description": "License identifier (e.g., MIT, Apache-2.0)" + }, + "compatibility": { + "type": "string", + "maxLength": 500, + "description": "Compatibility notes and requirements" + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Arbitrary key-value metadata (author, category, tags, ecosystem, etc.)" + }, + "allowed-tools": { + "type": "string", + "description": "Space-delimited list of pre-approved tool names (Agent Skills Spec)" + }, + "argument-hint": { + "type": "string", + "description": "Hint text shown when agents invoke this skill via slash command (Agent Skills Spec)" + }, + "user-invocable": { + "type": "boolean", + "default": true, + "description": "Whether agents can invoke this skill as a slash command (Agent Skills Spec)" + }, + "disable-model-invocation": { + "type": "boolean", + "default": false, + "description": "Whether to prevent auto-loading this skill based on context (Agent Skills Spec)" + }, + "location": { + "type": "string", + "format": "uri", + "pattern": "^file:///", + "description": "file:/// URI to a directory containing SKILL.md and supporting files" + }, + "tools": { + "type": "array", + "description": "Tools provided by this skill. Each tool is derived from a sibling adapter or defined as a local file instruction.", + "items": { "$ref": "#/$defs/SkillTool" }, + "minItems": 1 + } + }, + "required": ["name", "description"], + "additionalProperties": false +} +``` + +### SkillTool (New Type) + +```json +{ + "type": "object", + "description": "A tool declared within a skill. Derived from a sibling api or mcp adapter via 'from', or defined as a local file instruction.", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$", + "minLength": 1, + "maxLength": 64, + "description": "Tool identifier (kebab-case)" + }, + "description": { + "type": "string", + "description": "What the tool does. Used for agent discovery." + }, + "from": { + "type": "object", + "description": "Derive this tool from a sibling api or mcp adapter.", + "properties": { + "namespace": { + "type": "string", + "description": "Sibling exposes[].namespace (must be type api or mcp)" + }, + "action": { + "type": "string", + "description": "Operation name (api) or tool name (mcp) in the source adapter" + } + }, + "required": ["namespace", "action"], + "additionalProperties": false + }, + "instruction": { + "type": "string", + "description": "File path relative to the skill's location directory containing the tool instruction" + } + }, + "required": ["name", "description"], + "oneOf": [ + { "required": ["from"] }, + { "required": ["instruction"] } + ], + "additionalProperties": false +} +``` + +--- + +## Predefined REST Endpoints + +The `skill` server adapter automatically provides these **predefined** GET-only endpoints for discovery and distribution: + +### Endpoint Summary + +| HTTP | Path | Purpose | +|------|------|---------| +| GET | `/skills` | List all skills with tool summaries | +| GET | `/skills/{name}` | Skill metadata + tool catalog (derived with invocation refs, instruction with file paths) | +| GET | `/skills/{name}/download` | Skill package (ZIP) from `location` | +| GET | `/skills/{name}/contents` | File listing from `location` | +| GET | `/skills/{name}/contents/{file}` | Individual file from `location` | + +### 1. List All Skills + +``` +GET /skills +Response: application/json +{ + "count": 2, + "skills": [ + { + "name": "weather-forecast", + "description": "Look up weather forecasts by location name or coordinates", + "license": "MIT", + "tools": ["get-forecast", "resolve-and-forecast"] + }, + { + "name": "order-management", + "description": "Manage orders through the public API", + "tools": ["list-orders", "create-order", "cancel-order"] + } + ] +} +``` + +### 2. Get Skill Metadata (including tool catalog) + +``` +GET /skills/{name} +Response: application/json +{ + "name": "weather-forecast", + "description": "Look up weather forecasts by location name or coordinates", + "license": "MIT", + "compatibility": "Requires network access to weather and geocoding APIs", + "argument-hint": "Describe the location you want a forecast for", + "metadata": { + "author": "weather-team", + "category": "weather" + }, + "tools": [ + { + "name": "get-forecast", + "description": "Get weather forecast for a city", + "type": "derived", + "invocationRef": { + "targetNamespace": "weather-rest", + "action": "get-forecast", + "mode": "api" + }, + "inputSchema": { + "type": "object", + "properties": { + "city": { "type": "string", "description": "City name" } + }, + "required": ["city"] + } + }, + { + "name": "resolve-and-forecast", + "description": "Resolve a place name to coordinates, then fetch forecast", + "type": "derived", + "invocationRef": { + "targetNamespace": "weather-mcp", + "action": "resolve-and-forecast", + "mode": "mcp" + }, + "inputSchema": { + "type": "object", + "properties": { + "place": { "type": "string", "description": "Place name to resolve" } + }, + "required": ["place"] + } + }, + { + "name": "interpret-weather", + "description": "Guide for reading and interpreting weather data", + "type": "instruction", + "instruction": "interpret-weather.md" + } + ] +} +``` + +For **derived tools**, agents use `invocationRef` to invoke the tool directly through the source adapter. For **instruction tools**, agents read the instruction content via `GET /skills/{name}/contents/{file}`. The skill server does not proxy or execute tools. + +### 3–5. Download and File Browsing + +Files served from the skill's `location` directory: + +``` +GET /skills/{name}/download +→ ZIP archive of the location directory + +GET /skills/{name}/contents +→ { "name": "weather-forecast", "files": [ + { "path": "SKILL.md", "size": 2048, "type": "text/markdown" }, + { "path": "README.md", "size": 1024, "type": "text/markdown" }, + { "path": "examples/basic-usage.md", "size": 512, "type": "text/markdown" } + ]} + +GET /skills/{name}/contents/{file} +→ File content (MIME type based on extension) +``` + +If no `location` is configured, the download and contents endpoints return 404. + +--- + +## Visual Architecture + +### High-Level System Architecture + +``` ++-------------------------------------------------------------------+ +| NAFTIKO CAPABILITY | ++-------------------------------------------------------------------+ +| | +| CONSUMES (Backing HTTP APIs) | +| +-----------------------------------------------------------+ | +| | ConsumesHttp | | +| | -- namespace: "weather-api" | | +| | -- baseUri: "https://..." | | +| | -- resources/operations | | +| +-----------------------------------------------------------+ | +| | +| EXPOSES | +| +---------------------------+ +---------------------------+ | +| | ExposesApi | | ExposesMcp | | +| | type: "api" | | type: "mcp" | | +| | port: 9090 | | port: 9091 | | +| | namespace: "weather-rest" | | namespace: "weather-mcp" | | +| | resources / operations | | tools (call/steps/with) | | +| | (EXECUTES tools) | | (EXECUTES tools) | | +| +---------------------------+ +---------------------------+ | +| ^ ^ | +| | tools[].from | tools[].from | +| | | | +| +-----------------------------------------------------------+ | +| | ExposesSkill (DESCRIBES tools) | | +| | type: "skill" | | +| | port: 8080 | | +| | namespace: "weather-skills" | | +| | skills: [ tools: derived + instruction ] | | +| | location: "file:///..." | | +| +-----------------------------------------------------------+ | +| | ++-------------------------------------------------------------------+ + | + Predefined GET Endpoints + (discovery + distribution only) +``` + +### Three Adapter Types Comparison + +``` ++---------------------------+ +---------------------------+ +---------------------------+ +| API Adapter | | MCP Adapter | | SKILL Adapter | +| (EXECUTES) | | (EXECUTES) | | (DESCRIBES) | ++---------------------------+ +---------------------------+ +---------------------------+ +| ExposesApi | | ExposesMcp | | ExposesSkill | +| +- resources[] | | +- tools[] | | +- skills[] | +| | +- path | | | +- name | | | +- name | +| | +- operations[] | | | +- description | | | +- description | +| | +- method | | | +- inputParameters[] | | | +- frontmatter props | +| | +- call / steps | | | +- call / steps | | | +- location | +| | +- with | | | +- with | | | +- tools[] | +| | +- inputParams[] | | | +- outputParameters[] | | | +- name | +| | +- outputParams[] | | | | | | +- description | +| | | | | | +- from {ns, action}| +| Execution: HTTP endpoints | | Execution: MCP protocol | | | +- instruction | ++---------------------------+ +---------------------------+ | | + | (agents invoke adjacent | + | adapters directly) | + +---------------------------+ +``` + +### Discovery & Invocation Flow + +``` +AI Agent / Client + | + | 1. GET /skills/weather-forecast + | (discover tools — derived + instruction) + | + v ++-------------------------------------------+ +| Skill Server (port 8080) | +| Returns tool catalog: | +| - get-forecast (derived) | +| invocationRef: weather-rest (api) | +| - resolve-and-forecast (derived) | +| invocationRef: weather-mcp (mcp) | +| - interpret-weather (instruction) | +| instruction: interpret-weather.md | ++-------------------------------------------+ + | + | 2. Agent reads catalog, decides which tool to use + | + v ++-------------------------------------------+ +| Agent invokes derived tools DIRECTLY | +| through the source adapter. | +| Agent reads instruction tools via | +| GET /skills/{name}/contents/{file} | ++-------------------------------------------+ + | | + | API mode: | MCP mode: + | GET http://host:9090/ | MCP call to host:9091 + | forecast/London | tool: resolve-and-forecast + | | args: { place: "London" } + v v ++---------------------+ +---------------------+ +| API Adapter (9090) | | MCP Adapter (9091) | +| Executes via | | Executes via | +| call/steps/with | | call/steps/with | ++---------------------+ +---------------------+ + | | + v v ++-------------------------------------------+ +| Consumed HTTP APIs | +| (weather-api, geocoding-api) | ++-------------------------------------------+ +``` + +### Tool Resolution Flow + +``` ++---------------------------+ +---------------------------+ +| Sibling: ExposesApi | | Sibling: ExposesMcp | +| namespace: "public-api" | | namespace: "mcp-tools" | +| | | | +| resources: | | tools: | +| /orders | | - summarize-order | +| GET list-orders | | - format-report | +| POST create-order | | | +| /orders/{{id}} | | | +| DELETE cancel-order | | | ++---------------------------+ +---------------------------+ + ^ ^ + | | + | tools[].from | tools[].from + | ns: "public-api" | ns: "mcp-tools" + | | ++---------------------------------------------------+ +| ExposesSkill | +| namespace: "order-skills" | +| | +| skills: | +| - name: "order-management" | +| description: "Manage customer orders" | +| license: "Apache-2.0" | +| location: "file:///etc/naftiko/skills/orders" | +| | +| tools: | +| - name: "list-orders" (derived) | +| from: { ns: public-api } | +| - name: "create-order" (derived) | +| from: { ns: public-api } | +| - name: "summarize-order" (derived) | +| from: { ns: mcp-tools } | +| - name: "order-guidelines" (instruction) | +| instruction: order-guidelines.md | ++---------------------------------------------------+ ++---------------------------------------------------+ +``` + +### Endpoint Structure + +``` + Skill Server (port 8080) + GET-only endpoints + | + +-------------------+-------------------+ + | | + +------------------------------------------------+ + | GET /skills | + | Returns: All skills with tool summaries | + | { | + | "count": 2, | + | "skills": [ | + | { name, description, tools: [...] }, | + | ... | + | ] | + | } | + +------------------------------------------------+ + | | + +-- /weather-forecast +-- /order-management + | +- metadata + tool catalog | +- metadata + tool catalog + | +- download (from location) | +- download (from location) + | +- contents (from location) | +- contents (from location) + | | + | All tool invocations go DIRECTLY | + | to the source adapter, NOT through | + | the skill server. | +``` + +--- + +## Implementation Examples + +### Example 1: Weather Intelligence Skills + +**Scenario**: Catalog weather tools from REST API and MCP adapters as a discoverable skill. + +```yaml +naftiko: "0.5" + +info: + label: "Weather Intelligence Skills" + description: "Skills for weather forecasting — tools executed via adjacent adapters" + +externalRefs: + - name: "api-keys" + type: "variables" + resolution: "runtime" + keys: + weather_key: "WEATHER_API_KEY" + +capability: + consumes: + - type: "http" + namespace: "weather-api" + baseUri: "https://api.weather.com/v1/" + authentication: + type: "apikey" + key: "X-API-Key" + value: "{{weather_key}}" + placement: "header" + resources: + - path: "forecast/{{location}}" + name: "forecast" + operations: + - method: "GET" + name: "get-forecast" + outputParameters: + - name: "forecast" + type: "object" + value: "$.forecast" + + - type: "http" + namespace: "geocoding-api" + baseUri: "https://geocode.example.com/" + resources: + - path: "search/{{query}}" + name: "search" + operations: + - method: "GET" + name: "resolve-location" + outputParameters: + - name: "coordinates" + type: "object" + value: "$.result" + + exposes: + # API adapter — executes the forecast tool via REST + - type: "api" + address: "0.0.0.0" + port: 9090 + namespace: "weather-rest" + resources: + - path: "/forecast/{{city}}" + operations: + - method: "GET" + name: "get-forecast" + call: "weather-api.get-forecast" + with: + location: "{{city}}" + inputParameters: + - name: "city" + in: "path" + type: "string" + description: "City name (e.g. 'London', 'New York')" + + # MCP adapter — executes multi-step tools via MCP protocol + - type: "mcp" + transport: "http" + address: "0.0.0.0" + port: 9091 + namespace: "weather-mcp" + tools: + - name: "resolve-and-forecast" + description: "Resolve a place name to coordinates, then fetch forecast" + steps: + - type: "call" + name: "geo" + call: "geocoding-api.resolve-location" + with: + query: "{{place}}" + - type: "call" + name: "weather" + call: "weather-api.get-forecast" + with: + location: "{{geo.coordinates.lat}},{{geo.coordinates.lon}}" + inputParameters: + - name: "place" + type: "string" + description: "Place name to resolve (e.g. 'Eiffel Tower')" + mappings: + - targetName: "location" + value: "$.geo.coordinates" + - targetName: "forecast" + value: "$.weather.forecast" + outputParameters: + - name: "location" + type: "object" + - name: "forecast" + type: "object" + + # Skill adapter — catalogs tools from both adapters above + - type: "skill" + address: "0.0.0.0" + port: 8080 + namespace: "weather-skills" + description: "Weather intelligence skills for AI agents" + + skills: + - name: "weather-forecast" + description: "Look up weather forecasts by location name or coordinates" + license: "MIT" + compatibility: "Requires network access to weather and geocoding APIs" + argument-hint: "Describe the location you want a forecast for" + location: "file:///etc/naftiko/skills/weather-forecast" + metadata: + author: "weather-team" + category: "weather" + + tools: + - name: "get-forecast" + description: "Get weather forecast for a city" + from: + namespace: "weather-rest" + action: "get-forecast" + - name: "resolve-and-forecast" + description: "Resolve a place name to coordinates, then fetch forecast" + from: + namespace: "weather-mcp" + action: "resolve-and-forecast" + - name: "interpret-weather" + description: "Guide for reading and interpreting weather data" + instruction: "interpret-weather.md" +``` + +#### Test Commands +```bash +# List all skills +curl http://localhost:8080/skills | jq '.' + +# Get skill metadata with tool catalog +curl http://localhost:8080/skills/weather-forecast | jq '.' + +# Browse supporting files (served from location) +curl http://localhost:8080/skills/weather-forecast/contents | jq '.' + +# Read instruction tool content +curl http://localhost:8080/skills/weather-forecast/contents/interpret-weather.md + +# Agent invokes derived tools DIRECTLY through source adapter: +# Via REST API adapter: +curl http://localhost:9090/forecast/London | jq '.' + +# Via MCP adapter (using MCP protocol, not curl): +# mcp call weather-mcp resolve-and-forecast '{"place": "Eiffel Tower"}' +``` + +#### Expected Responses + +**GET /skills:** +```json +{ + "count": 1, + "skills": [ + { + "name": "weather-forecast", + "description": "Look up weather forecasts by location name or coordinates", + "license": "MIT", + "tools": ["get-forecast", "resolve-and-forecast", "interpret-weather"] + } + ] +} +``` + +**GET /skills/weather-forecast:** +```json +{ + "name": "weather-forecast", + "description": "Look up weather forecasts by location name or coordinates", + "license": "MIT", + "compatibility": "Requires network access to weather and geocoding APIs", + "argument-hint": "Describe the location you want a forecast for", + "metadata": { + "author": "weather-team", + "category": "weather" + }, + "tools": [ + { + "name": "get-forecast", + "description": "Get weather forecast for a city", + "type": "derived", + "invocationRef": { + "targetNamespace": "weather-rest", + "action": "get-forecast", + "mode": "api" + }, + "inputSchema": { + "type": "object", + "properties": { + "city": { "type": "string", "description": "City name (e.g. 'London', 'New York')" } + }, + "required": ["city"] + } + }, + { + "name": "resolve-and-forecast", + "description": "Resolve a place name to coordinates, then fetch forecast", + "type": "derived", + "invocationRef": { + "targetNamespace": "weather-mcp", + "action": "resolve-and-forecast", + "mode": "mcp" + }, + "inputSchema": { + "type": "object", + "properties": { + "place": { "type": "string", "description": "Place name to resolve (e.g. 'Eiffel Tower')" } + }, + "required": ["place"] + } + }, + { + "name": "interpret-weather", + "description": "Guide for reading and interpreting weather data", + "type": "instruction", + "instruction": "interpret-weather.md" + } + ] +} +``` + +--- + +### Example 2: Order Management — Tools from API Adapter + +**Scenario**: Catalog existing API operations as discoverable skill tools. + +```yaml +naftiko: "0.5" + +info: + label: "Order Management Platform" + description: "Skills derived from existing API adapters" + +capability: + consumes: + - type: "http" + namespace: "orders-backend" + baseUri: "https://api.company.com/v2/" + resources: + - path: "orders" + name: "orders" + operations: + - method: "GET" + name: "list-orders" + outputParameters: + - name: "orders" + type: "array" + value: "$.orders" + - method: "POST" + name: "create-order" + outputParameters: + - name: "order" + type: "object" + value: "$.order" + - path: "orders/{{id}}" + name: "order" + operations: + - method: "GET" + name: "get-order" + outputParameters: + - name: "order" + type: "object" + value: "$.order" + - method: "DELETE" + name: "cancel-order" + + exposes: + # Sibling API adapter — executes tools + - type: "api" + address: "0.0.0.0" + port: 9090 + namespace: "public-api" + resources: + - path: "/orders" + operations: + - method: "GET" + name: "list-orders" + call: "orders-backend.list-orders" + - method: "POST" + name: "create-order" + call: "orders-backend.create-order" + - path: "/orders/{{id}}" + operations: + - method: "GET" + name: "get-order" + call: "orders-backend.get-order" + - method: "DELETE" + name: "cancel-order" + call: "orders-backend.cancel-order" + + # Skill adapter — catalogs tools from API adapter + - type: "skill" + address: "0.0.0.0" + port: 8080 + namespace: "order-skills" + description: "Order management skills" + + skills: + - name: "order-management" + description: "Manage customer orders through the API" + license: "Apache-2.0" + location: "file:///etc/naftiko/skills/order-management" + tools: + - name: "list-orders" + description: "List all customer orders" + from: + namespace: "public-api" + action: "list-orders" + - name: "get-order" + description: "Get details of a specific order" + from: + namespace: "public-api" + action: "get-order" + - name: "create-order" + description: "Create a new customer order" + from: + namespace: "public-api" + action: "create-order" + + - name: "order-admin" + description: "Administrative order operations" + user-invocable: false + tools: + - name: "cancel-order" + description: "Cancel an existing order" + from: + namespace: "public-api" + action: "cancel-order" +``` + +#### Expected GET /skills/order-management Response +```json +{ + "name": "order-management", + "description": "Manage customer orders through the API", + "license": "Apache-2.0", + "tools": [ + { + "name": "list-orders", + "description": "List all customer orders", + "type": "derived", + "invocationRef": { + "targetNamespace": "public-api", + "action": "list-orders", + "mode": "api" + } + }, + { + "name": "get-order", + "description": "Get details of a specific order", + "type": "derived", + "invocationRef": { + "targetNamespace": "public-api", + "action": "get-order", + "mode": "api" + } + }, + { + "name": "create-order", + "description": "Create a new customer order", + "type": "derived", + "invocationRef": { + "targetNamespace": "public-api", + "action": "create-order", + "mode": "api" + } + } + ] +} +``` + +--- + +### Example 3: Multi-Adapter Tools (API + MCP + Instruction) + +**Scenario**: Declare tools from both REST API and MCP adapters plus a local instruction in one skill. + +```yaml +naftiko: "0.5" + +info: + label: "Data Intelligence Platform" + description: "Skills with tools from REST and MCP adapters plus instructions" + +capability: + consumes: + - type: "http" + namespace: "analytics-api" + baseUri: "https://analytics.example.com/" + resources: + - path: "analyze" + name: "analyze" + operations: + - method: "POST" + name: "run-analysis" + outputParameters: + - name: "analysis" + type: "object" + value: "$.result" + + exposes: + # REST API adapter + - type: "api" + address: "0.0.0.0" + port: 9090 + namespace: "analytics-rest" + resources: + - path: "/analyze" + operations: + - method: "POST" + name: "run-analysis" + call: "analytics-api.run-analysis" + + # MCP adapter + - type: "mcp" + transport: "stdio" + namespace: "analytics-mcp" + tools: + - name: "quick-stats" + description: "Run quick statistical analysis" + call: "analytics-api.run-analysis" + inputParameters: + - name: "data" + type: "object" + description: "Data to analyze" + + # Skill adapter — tools from both adapters + local instruction + - type: "skill" + address: "0.0.0.0" + port: 8080 + namespace: "data-skills" + description: "Data intelligence skills" + + skills: + - name: "data-analysis" + description: "Data analysis tools from REST and MCP" + license: "MIT" + allowed-tools: "run-analysis quick-stats" + location: "file:///etc/naftiko/skills/data-analysis" + tools: + - name: "run-analysis" + description: "Run a full data analysis via REST" + from: + namespace: "analytics-rest" + action: "run-analysis" + - name: "quick-stats" + description: "Run quick statistical analysis via MCP" + from: + namespace: "analytics-mcp" + action: "quick-stats" + - name: "analysis-methodology" + description: "Guide for choosing the right analysis approach" + instruction: "methodology.md" +``` + +--- + +### Example 4: Kubernetes Deployment + +**Scenario**: Production deployment of skill server alongside API and MCP adapters. + +#### Docker Compose +```yaml +version: '3.8' + +services: + naftiko-platform: + image: naftiko:latest + ports: + - "8080:8080" # Skill catalog + - "9090:9090" # REST API (tool execution) + - "9091:9091" # MCP (tool execution) + environment: + WEATHER_API_KEY: ${WEATHER_API_KEY} + volumes: + - ./capability.yaml:/etc/naftiko/capability.yaml + - ./skills/:/etc/naftiko/skills/ # Skill supporting files + command: naftiko run /etc/naftiko/capability.yaml +``` + +#### Kubernetes Deployment +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: naftiko-skill-server + namespace: production +spec: + replicas: 3 + selector: + matchLabels: + app: skill-server + template: + metadata: + labels: + app: skill-server + spec: + containers: + - name: naftiko + image: naftiko:v1.5.0 + ports: + - containerPort: 8080 + name: skill-catalog + - containerPort: 9090 + name: rest-api + - containerPort: 9091 + name: mcp + env: + - name: NAFTIKO_CONFIG + value: /etc/naftiko/capability.yaml + - name: WEATHER_API_KEY + valueFrom: + secretKeyRef: + name: api-credentials + key: weather-api-key + volumeMounts: + - name: config + mountPath: /etc/naftiko + - name: skills + mountPath: /etc/naftiko/skills + livenessProbe: + httpGet: + path: /skills + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /skills + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + volumes: + - name: config + configMap: + name: skill-server-config + - name: skills + persistentVolumeClaim: + claimName: skill-files +--- +apiVersion: v1 +kind: Service +metadata: + name: skill-server + namespace: production +spec: + selector: + app: skill-server + ports: + - port: 80 + targetPort: 8080 + name: skill-catalog + - port: 9090 + targetPort: 9090 + name: rest-api + - port: 9091 + targetPort: 9091 + name: mcp + type: ClusterIP +``` + +--- + +## Security Considerations + +### Input Validation +- `{name}` parameter: `^[a-zA-Z0-9_-]+$` +- `{file}` path: validated against path traversal (no `../`) +- All path segments restricted by character whitelist + +### Location URI Validation +- Only `file:///` scheme is permitted +- Resolved path must stay within allowed base directories +- Symlinks resolved and validated against directory boundaries +- No relative path components (`..`) allowed in resolved URI + +### Authentication +- API Key (header, query) +- Bearer token +- Basic authentication +- OAuth2 + +### File Access Control +- Restrict file access to skill `location` directory trees +- No parent directory access (`../`) +- Validate all file paths for traversal attacks + +--- + +## Tool Validation Rules + +1. `tools` is optional — a skill can be purely descriptive (metadata + `location` only, no tools) +2. Each tool must specify exactly one source: `from` (derived) or `instruction` (local file) +3. For derived tools (`from`), `namespace` must resolve to exactly one sibling `exposes[].namespace` of type `api` or `mcp` +4. Referencing a `skill`-type adapter from `from.namespace` is invalid (no recursive derivation) +5. For derived tools, `action` must exist as an operation name (api) or tool name (mcp) in the resolved adapter +6. For instruction tools, the skill must have a `location` configured — the instruction path is resolved relative to it +7. Tool `name` values must be unique within a skill + +--- + +## Implementation Roadmap + +| Phase | Deliverable | +|-------|-------------| +| Phase 1 | Core schema + ExposedSkill + SkillTool types + validation | +| Phase 2 | Discovery endpoints (GET /skills, GET /skills/{name}) + tool resolution | +| Phase 3 | Location support + file browsing + download endpoints | +| Phase 4 | Auth, caching, testing, documentation, examples | + +--- + +## Backward Compatibility + +- No breaking changes +- Purely additive to Naftiko 0.5+ +- Existing capabilities unaffected +- New `type: "skill"` in exposes union + +--- + +## Why This Architecture Works + +1. **Clear Separation of Concerns**: Skills describe; `api` and `mcp` adapters execute. Each adapter does one thing well. + +2. **No Duplication**: Derived tools reference operations already defined in adjacent adapters — no need to redefine tool logic. Instruction tools add knowledge without duplicating execution. + +3. **Agent Skills Spec Alignment**: Full frontmatter metadata (name, description, license, compatibility, argument-hint, invocation controls) aligned with the [Agent Skills Spec](https://agentskills.io/specification). + +4. **Supporting Files**: `location` property provides SKILL.md and supporting documentation, served through REST endpoints. + +5. **Direct Invocation**: Agents discover tools through the skill catalog, then invoke the source adapter directly — no proxy overhead, no execution complexity in the skill layer. + +6. **Composable**: A skill can declare derived tools from multiple sibling adapters (both `api` and `mcp`) and instruction tools from local files, providing a unified discovery surface. + +7. **Enterprise Ready**: Auth, metadata governance, and file distribution endpoints support internal hosting and access control. diff --git a/src/main/resources/blueprints/agent-skills-support-schema-amendments.json b/src/main/resources/blueprints/agent-skills-support-schema-amendments.json new file mode 100644 index 0000000..a3651a3 --- /dev/null +++ b/src/main/resources/blueprints/agent-skills-support-schema-amendments.json @@ -0,0 +1,653 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Agent Skills Specification - Schema Amendments", + "description": "Schema amendments to capability-schema.json for skill adapter support. The skill adapter is a metadata and catalog layer — skills declare tools derived from sibling api and mcp adapters or defined as local file instructions. Derived tools are not executed by the skill adapter; AI clients invoke the source adapters directly.", + "version": "4.0.0", + "metadata": { + "created": "2026-03-05", + "framework": "Naftiko", + "adapter_type": "skill", + "pattern": "Skill metadata & catalog adapter (peer to api, mcp). Skills declare tools (derived or instruction-based) — does not execute derived tools." + }, + "amendments": { + "description": "Apply these type definitions to capability-schema.json. Skills declare tools derived from sibling api and mcp adapters or defined as local file instructions. Skills can also stand alone as purely descriptive (no tools). Full Agent Skills Spec frontmatter metadata on ExposedSkill. Location property for supporting files." + }, + "types": { + "ExposesSkills": { + "$id": "#/definitions/ExposesSkills", + "type": "object", + "title": "ExposesSkills", + "description": "Skill server adapter — skills declare tools derived from sibling adapters or as local file instructions. Metadata and catalog layer peer to api and mcp. Predefined GET-only REST endpoints for discovery and distribution.", + "additionalProperties": false, + "required": ["type", "port", "namespace", "skills"], + "properties": { + "type": { + "type": "string", + "const": "skill", + "description": "Must be 'skill' to create a skill catalog server" + }, + "address": { + "type": "string", + "description": "Server bind address (e.g., '0.0.0.0', '127.0.0.1')", + "examples": ["0.0.0.0", "127.0.0.1"] + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535, + "description": "Server port" + }, + "namespace": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$", + "minLength": 1, + "maxLength": 64, + "description": "Unique identifier for this skill server" + }, + "description": { + "type": "string", + "description": "Human-readable description of this skill server" + }, + "authentication": { + "$ref": "#/definitions/Authentication", + "description": "Optional: Require authentication for all endpoints" + }, + "skills": { + "type": "array", + "description": "Array of skill definitions. Each skill declares tools (derived from sibling adapters or local file instructions), or stands alone as purely descriptive.", + "minItems": 1, + "items": { + "$ref": "#/definitions/ExposedSkill" + } + }, + "cors": { + "type": "object", + "additionalProperties": false, + "description": "Optional: CORS settings", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable CORS. Default: true" + }, + "allowedOrigins": { + "type": "array", + "items": { "type": "string" }, + "description": "Allowed origins. Default: ['*']" + }, + "allowedMethods": { + "type": "array", + "items": { "type": "string" }, + "description": "Allowed methods. Default: ['GET', 'HEAD', 'OPTIONS']" + } + } + } + } + }, + "ExposedSkill": { + "$id": "#/definitions/ExposedSkill", + "type": "object", + "title": "ExposedSkill", + "description": "A skill definition. Declares tools derived from sibling api or mcp adapters or defined as local file instructions. Can also stand alone as purely descriptive (no tools). Supports full Agent Skills Spec frontmatter metadata.", + "additionalProperties": false, + "required": ["name", "description"], + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$", + "minLength": 1, + "maxLength": 64, + "description": "Skill identifier (kebab-case)" + }, + "description": { + "type": "string", + "maxLength": 1024, + "description": "What the skill does and when to use it. Used for agent discovery." + }, + "license": { + "type": "string", + "description": "License identifier (e.g., MIT, Apache-2.0) — Agent Skills Spec metadata" + }, + "compatibility": { + "type": "string", + "maxLength": 500, + "description": "Compatibility notes and requirements — Agent Skills Spec metadata" + }, + "metadata": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Arbitrary key-value metadata (author, category, tags, ecosystem, etc.)" + }, + "allowed-tools": { + "type": "string", + "description": "Space-delimited list of pre-approved tool names — Agent Skills Spec metadata" + }, + "argument-hint": { + "type": "string", + "description": "Hint text shown when agents invoke this skill via slash command — Agent Skills Spec metadata" + }, + "user-invocable": { + "type": "boolean", + "default": true, + "description": "Whether agents can invoke this skill as a slash command — Agent Skills Spec metadata" + }, + "disable-model-invocation": { + "type": "boolean", + "default": false, + "description": "Whether to prevent auto-loading this skill based on context — Agent Skills Spec metadata" + }, + "location": { + "type": "string", + "format": "uri", + "pattern": "^file:///", + "description": "file:/// URI to a directory containing SKILL.md and supporting files/folders. Served through the /contents and /download endpoints." + }, + "tools": { + "type": "array", + "description": "Tools provided by this skill. Each tool is derived from a sibling api or mcp adapter via 'from', or defined as a local file instruction relative to the skill's location directory.", + "minItems": 1, + "items": { + "$ref": "#/definitions/SkillTool" + } + } + } + }, + "SkillTool": { + "$id": "#/definitions/SkillTool", + "type": "object", + "title": "SkillTool", + "description": "A tool declared within a skill. Derived from a sibling api or mcp adapter via 'from', or defined as a local file instruction relative to the skill's location directory.", + "additionalProperties": false, + "required": ["name", "description"], + "oneOf": [ + { "required": ["from"] }, + { "required": ["instruction"] } + ], + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$", + "minLength": 1, + "maxLength": 64, + "description": "Tool identifier (kebab-case)" + }, + "description": { + "type": "string", + "description": "What the tool does. Used for agent discovery." + }, + "from": { + "type": "object", + "additionalProperties": false, + "description": "Derive this tool from a sibling api or mcp adapter. The adapter type (api/mcp) is inferred from the resolved target.", + "required": ["namespace", "action"], + "properties": { + "namespace": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$", + "description": "Sibling exposes[].namespace of type 'api' or 'mcp'. No derivation from other skill adapters." + }, + "action": { + "type": "string", + "description": "Operation name (api) or tool name (mcp) in the source adapter" + } + } + }, + "instruction": { + "type": "string", + "description": "File path relative to the skill's location directory containing the tool instruction. The skill must have a location configured." + } + } + } + }, + "predefined_endpoints": { + "description": "Auto-generated GET-only endpoints created by ExposesSkills adapter. The skill server provides discovery and distribution — no tool execution.", + "endpoints": [ + { + "method": "GET", + "path": "/skills", + "description": "List all skills with tool summaries", + "response": { + "type": "object", + "properties": { + "count": { "type": "integer" }, + "skills": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "license": { "type": "string" }, + "tools": { + "type": "array", + "items": { "type": "string" }, + "description": "List of tool names declared by this skill" + } + } + } + } + } + } + }, + { + "method": "GET", + "path": "/skills/{name}", + "description": "Get skill metadata including tool catalog. Tools are either derived (with invocationRef for direct invocation) or instruction-based (with file path to instruction).", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "Skill name" + } + ], + "response": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "license": { "type": "string" }, + "compatibility": { "type": "string" }, + "argument-hint": { "type": "string" }, + "metadata": { "type": "object" }, + "tools": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "type": { "type": "string", "enum": ["derived", "instruction"], "description": "Tool source type" }, + "invocationRef": { + "type": "object", + "description": "Present on derived tools. Reference to the source adapter and action for direct invocation.", + "properties": { + "targetNamespace": { "type": "string", "description": "Source adapter namespace" }, + "action": { "type": "string", "description": "Operation or tool name in the source adapter" }, + "mode": { "type": "string", "enum": ["api", "mcp"], "description": "Source adapter type" } + } + }, + "inputSchema": { + "type": "object", + "description": "Present on derived tools. JSON Schema from source operation/tool input parameters." + }, + "instruction": { + "type": "string", + "description": "Present on instruction tools. File path relative to the skill's location directory." + } + } + } + } + } + } + }, + { + "method": "GET", + "path": "/skills/{name}/download", + "description": "Download skill package as ZIP from the skill's location directory (SKILL.md + supporting files). Returns 404 if no location is configured.", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true + } + ], + "response": { + "type": "binary", + "mimeType": "application/zip" + } + }, + { + "method": "GET", + "path": "/skills/{name}/contents", + "description": "List files in skill's location directory. Returns 404 if no location is configured.", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true + } + ], + "response": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { "type": "string" }, + "size": { "type": "integer" }, + "type": { "type": "string" } + } + } + } + } + } + }, + { + "method": "GET", + "path": "/skills/{name}/contents/{file}", + "description": "Get individual skill file content from the skill's location directory. Returns 404 if no location is configured.", + "parameters": [ + { + "name": "name", + "in": "path", + "required": true + }, + { + "name": "file", + "in": "path", + "required": true, + "description": "File path (URL encoded)" + } + ], + "response": { + "type": "string", + "description": "File content (MIME type based on file extension)" + } + } + ] + }, + "path_validation": { + "description": "Security constraints for path parameters", + "rules": [ + { + "parameter": "name", + "pattern": "^[a-zA-Z0-9_-]+$", + "description": "Alphanumeric with underscores and hyphens only" + }, + { + "parameter": "file", + "pattern": "^(?!\\.\\./)(.+)$", + "description": "No path traversal (../ not allowed). URL encoded." + } + ], + "security": [ + { + "rule": "Validate all path segments", + "description": "Reject any path containing ../, ..\\, or null bytes" + }, + { + "rule": "Character whitelist only", + "description": "Only allow [a-zA-Z0-9._/-] in file paths" + }, + { + "rule": "Size limits", + "description": "ZIP download limit: 500MB. File content limit: 10MB." + }, + { + "rule": "Location URI validation", + "description": "Only file:/// scheme permitted. Resolved path must stay within allowed base directories. No relative path components (..) allowed." + } + ] + }, + "tool_validation_rules": { + "description": "Validation and behavior rules for ExposedSkill.tools", + "validation": [ + { + "rule": "Tools are optional", + "constraint": "tools is optional — a skill can be purely descriptive (metadata + location only). When tools is present, it must contain at least one entry." + }, + { + "rule": "Tool source required", + "constraint": "Each tool must have exactly one of 'from' (derived) or 'instruction' (local file)." + }, + { + "rule": "From namespace resolution", + "constraint": "Every from.namespace must resolve to exactly one sibling exposes[].namespace of type api or mcp." + }, + { + "rule": "No recursive derivation", + "constraint": "from.namespace must not reference a skill-type expose. Only api and mcp adapters can be sources." + }, + { + "rule": "From action validation", + "constraint": "from.action must exist as an operation (api) or tool (mcp) in the resolved source adapter." + }, + { + "rule": "Instruction requires location", + "constraint": "If a tool uses 'instruction', the skill must have a location configured. The instruction path is relative to the location directory." + }, + { + "rule": "Unique tool names", + "constraint": "Tool names must be unique within a skill." + } + ], + "derived_tool_response_shape": { + "description": "Derived tools include an invocationRef so agents can invoke the source adapter directly. No tool execution happens through the skill server.", + "example": { + "name": "get-orders", + "description": "Retrieve orders", + "type": "derived", + "invocationRef": { + "targetNamespace": "public-api", + "action": "get-orders", + "mode": "api" + }, + "inputSchema": { + "type": "object", + "properties": {}, + "description": "Copied from source operation/tool input parameters" + } + } + }, + "instruction_tool_response_shape": { + "description": "Instruction tools reference a local file in the skill's location directory. Agents read the instruction content via the /contents endpoint.", + "example": { + "name": "interpret-data", + "description": "Guide for interpreting analysis results", + "type": "instruction", + "instruction": "instructions/interpret-data.md" + } + } + }, + "capability_schema_updates": { + "description": "Apply the following changes to capability-schema.json", + "changes": [ + { + "location": "definitions.Capability.properties.exposes.items.oneOf", + "action": "add", + "value": { "$ref": "#/definitions/ExposesSkills" }, + "description": "Add ExposesSkills to exposed server types union" + }, + { + "location": "definitions", + "action": "add_definitions", + "values": [ + "ExposesSkills", + "ExposedSkill", + "SkillTool" + ], + "description": "Add new type definitions to schema" + } + ] + }, + "validation_examples": { + "valid_tools_from_api": { + "_comment": "Tools derived from a sibling API adapter", + "type": "skill", + "address": "0.0.0.0", + "port": 8080, + "namespace": "my-skills", + "skills": [ + { + "name": "order-management", + "description": "Manage customer orders through the API", + "license": "Apache-2.0", + "location": "file:///etc/naftiko/skills/order-management", + "tools": [ + { + "name": "list-orders", + "description": "List all customer orders", + "from": { "namespace": "public-api", "action": "list-orders" } + }, + { + "name": "create-order", + "description": "Create a new customer order", + "from": { "namespace": "public-api", "action": "create-order" } + } + ] + } + ] + }, + "valid_tools_mixed": { + "_comment": "Mix of derived tools (API + MCP) and instruction tools", + "type": "skill", + "address": "0.0.0.0", + "port": 8080, + "namespace": "unified-skills", + "skills": [ + { + "name": "data-intelligence", + "description": "Unified data tools from REST and MCP adapters plus local instructions", + "allowed-tools": "run-analysis quick-stats interpret-data", + "argument-hint": "Describe the analysis you need", + "location": "file:///etc/naftiko/skills/data-intelligence", + "tools": [ + { + "name": "run-analysis", + "description": "Run a full data analysis", + "from": { "namespace": "analytics-rest", "action": "run-analysis" } + }, + { + "name": "quick-stats", + "description": "Run quick statistical analysis", + "from": { "namespace": "analytics-mcp", "action": "quick-stats" } + }, + { + "name": "interpret-data", + "description": "Guide for interpreting analysis results", + "instruction": "instructions/interpret-data.md" + } + ] + } + ] + }, + "valid_full_frontmatter": { + "_comment": "Full Agent Skills Spec frontmatter with all metadata properties", + "type": "skill", + "address": "0.0.0.0", + "port": 8080, + "namespace": "weather-skills", + "skills": [ + { + "name": "weather-forecast", + "description": "Look up weather forecasts by location name or coordinates", + "license": "MIT", + "compatibility": "Requires network access to weather and geocoding APIs", + "argument-hint": "Describe the location you want a forecast for", + "user-invocable": true, + "disable-model-invocation": false, + "allowed-tools": "get-forecast resolve-and-forecast interpret-weather", + "metadata": { + "author": "weather-team", + "category": "weather", + "version": "1.0.0" + }, + "location": "file:///etc/naftiko/skills/weather-forecast", + "tools": [ + { + "name": "get-forecast", + "description": "Get weather forecast for a city", + "from": { "namespace": "weather-rest", "action": "get-forecast" } + }, + { + "name": "resolve-and-forecast", + "description": "Resolve a place name to coordinates then fetch forecast", + "from": { "namespace": "weather-mcp", "action": "resolve-and-forecast" } + }, + { + "name": "interpret-weather", + "description": "Guide for reading and interpreting weather data", + "instruction": "interpret-weather.md" + } + ] + } + ] + }, + "valid_instruction_only": { + "_comment": "Skill with only instruction-based tools", + "type": "skill", + "address": "0.0.0.0", + "port": 8080, + "namespace": "docs-skills", + "skills": [ + { + "name": "coding-guidelines", + "description": "Company coding guidelines and best practices for AI agents", + "license": "proprietary", + "compatibility": "All projects", + "argument-hint": "Ask about coding standards", + "location": "file:///etc/naftiko/skills/coding-guidelines", + "metadata": { + "author": "platform-team", + "category": "documentation" + }, + "tools": [ + { + "name": "naming-conventions", + "description": "Naming conventions for variables, functions, and classes", + "instruction": "naming-conventions.md" + }, + { + "name": "error-handling", + "description": "Error handling patterns and best practices", + "instruction": "error-handling.md" + } + ] + } + ] + }, + "valid_descriptive_only": { + "_comment": "Purely descriptive skill — metadata + supporting files, no tools", + "type": "skill", + "address": "0.0.0.0", + "port": 8080, + "namespace": "docs-skills", + "skills": [ + { + "name": "onboarding-guide", + "description": "New developer onboarding guide", + "license": "proprietary", + "location": "file:///etc/naftiko/skills/onboarding", + "metadata": { + "author": "platform-team", + "category": "onboarding" + } + } + ] + }, + "invalid_from_recursive": { + "_comment": "Invalid: from references another skill adapter", + "error": "Cannot derive from skill-type adapters (no recursive derivation)", + "example": { + "name": "recursive-tool", + "description": "Attempting recursive derivation", + "tools": [ + { + "name": "bad-tool", + "description": "Tool from another skill", + "from": { "namespace": "other-skill-server", "action": "some-tool" } + } + ] + } + }, + "invalid_instruction_no_location": { + "_comment": "Invalid: instruction tool without skill location", + "error": "Instruction tools require the skill to have a location configured", + "example": { + "name": "no-location", + "description": "Skill with instruction tool but no location", + "tools": [ + { + "name": "guide", + "description": "A guide", + "instruction": "guide.md" + } + ] + } + } + } +} diff --git a/src/main/resources/blueprints/gap-analysis-report.md b/src/main/resources/blueprints/gap-analysis-report.md new file mode 100644 index 0000000..0ce2f71 --- /dev/null +++ b/src/main/resources/blueprints/gap-analysis-report.md @@ -0,0 +1,923 @@ +# Naftiko Framework: Specification vs Implementation Gap Analysis + +**Framework Version**: 0.5 +**Analysis Date**: March 5, 2026 +**Scope**: Complete gap analysis of Naftiko specification features against Java implementation + +--- + +## Executive Summary + +The Naftiko framework has **strong implementation coverage** of core specification features. Approximately **85-90% of the v0.5 specification is fully implemented** with complete support for exposition types, consumption mechanisms, authentication, request/response handling, and advanced orchestration features including multi-step operations and lookups. + +**Key Gaps Identified**: +- Conditional routing logic (if/then/else) - NOT in current spec but mentioned in v0.2 +- Advanced error handling and recovery strategies +- Async/parallel operation execution +- Built-in caching and rate limiting + +--- + +## 1. EXPOSITION TYPES + +### 1.1 REST API Exposition (`type: api`) + +**Spec Definition** (v0.5): +- Address and port binding +- Resource and operation definitions +- Input/output parameters +- Authentication support +- Request method: GET, POST, PUT, PATCH, DELETE + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Implementation Details**: +- **Location**: [engine/exposes/ApiServerAdapter.java](engine/exposes/ApiServerAdapter.java), [engine/exposes/ApiResourceRestlet.java](engine/exposes/ApiResourceRestlet.java) +- **Server Framework**: Restlet + Jetty +- **Features Implemented**: + - Resource and operation path routing with placeholder support + - HTTP method dispatch (GET, POST, PUT, PATCH, DELETE) + - Input parameter extraction from query, path, header, cookie, body + - Output parameter mapping to JSON response + - Authentication enforcement (see Section 3) + - Simple mode (single call) and orchestrated mode (multi-step) + - Forward configuration support + +**Code Examples**: +```java +// API operation execution path: +ApiServerAdapter.java -> startServer() -> creates Restlet chain +ApiResourceRestlet.java -> handle() -> resolves input parameters +OperationStepExecutor.java -> executeSteps() -> orchestrates calls +``` + +**Testing**: Verified in integration tests across multiple protocols (YAML, JSON, Avro, CSV, etc.) + +--- + +### 1.2 MCP HTTP Exposition (`type: mcp`, `transport: http`) + +**Spec Definition** (v0.5): +- Streamable HTTP transport +- Tools mapping to consumed operations +- Input parameters as JSON schema +- Tool call handler + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Implementation Details**: +- **Location**: [engine/exposes/McpServerAdapter.java](engine/exposes/McpServerAdapter.java), [engine/exposes/JettyMcpStreamableHandler.java](engine/exposes/JettyMcpStreamableHandler.java) +- **Protocol**: Custom Streamable HTTP (not standard HTTP/REST) +- **Features Implemented**: + - MCP protocol dispatcher + - Tool definition exposure as MCP tools + - JSON-RPC request/response handling + - Tool call execution with orchestrated steps support (same as API) + - Integration with step executor + +**Code Examples**: +```java +// MCP HTTP execution path: +McpServerAdapter.java -> startServer() -> Jetty with JettyMcpStreamableHandler +McpToolHandler.java -> handleToolCall() -> delegates to OperationStepExecutor +``` + +--- + +### 1.3 MCP Stdio Exposition (`type: mcp`, `transport: stdio`) + +**Spec Definition** (v0.5): +- STDIN/STDOUT JSON-RPC transport +- Interactive CLI integration (IDE) +- Tools as MCP capabilities + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Implementation Details**: +- **Location**: [engine/exposes/StdioJsonRpcHandler.java](engine/exposes/StdioJsonRpcHandler.java) +- **Protocol**: JSON-RPC 2.0 over STDIN/STDOUT +- **Features Implemented**: + - JSON-RPC message parsing + - Tool invocation handling + - Step execution within stdio transport + - Proper error response formatting + +**Code Examples**: +```java +// MCP Stdio execution path: +McpServerAdapter.java -> startServer() -> StdioJsonRpcHandler +JSON-RPC messages -> McpToolHandler -> OperationStepExecutor +``` + +--- + +## 2. CONSUMPTION TYPES + +### 2.1 HTTP Client (`type: http`) + +**Spec Definition** (v0.5): +- Base URI configuration +- Resources and operations +- HTTP methods: GET, POST, PUT, PATCH, DELETE +- Request bodies (JSON, text, form, multipart, raw) +- Output format handling (JSON, XML, CSV, YAML, Avro, Protobuf) +- Output parameter extraction with JsonPath +- Input/output parameters + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Implementation Details**: +- **Location**: [engine/consumes/HttpClientAdapter.java](engine/consumes/HttpClientAdapter.java) +- **HTTP Client Library**: Apache HttpClient (via Restlet) +- **Features Implemented**: + - Full request construction (URI, method, headers, body) + - Template resolution (Mustache) in URLs and parameters + - Request body serialization (all types) + - Response parsing (all formats with Converter) + - JsonPath extraction for output parameters + - Parameter validation and type checking + +**Request Body Types Implemented**: +- ✅ JSON (object, array, string) +- ✅ Text (text, xml, sparql variants) +- ✅ Form URL-encoded (object or string) +- ✅ Multipart Form (parts with name, value, filename, contentType) +- ✅ Raw (string passthrough) + +**Code Examples**: +```java +// HTTP request construction: +HttpClientAdapter.java -> createRequest() -> resolves templates +Resolver.resolveMustacheTemplate() -> expands {{variables}} +RequestBuilder -> constructs with authentication, body, headers +``` + +--- + +## 3. AUTHENTICATION TYPES + +**Spec Definition** (v0.5): +- Basic Auth (username/password) +- API Key Auth (header or query placement) +- Bearer Token Auth +- Digest Auth (username/password) + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Implementation Details**: +- **Location**: [spec/consumes/AuthenticationSpec.java](spec/consumes/AuthenticationSpec.java) and subclasses +- **Deserialization**: Custom deserializers handle polymorphic type mapping +- **Application Point**: HttpClientAdapter applies auth to all client requests + +**Each Authentication Type**: + +| Type | Placement | Implementation | Status | +|------|-----------|-----------------|--------| +| **Basic** | HTTP Authorization header | Standard Base64 encoding | ✅ Full | +| **Bearer** | HTTP Authorization header | "Bearer {token}" format | ✅ Full | +| **ApiKey** | Header or Query parameter | Custom location (key/value pair) | ✅ Full | +| **Digest** | HTTP Authorization header | RFC 7616 Digest Auth | ✅ Full | + +**Code Examples**: +```java +// Authentication application: +HttpClientAdapter.applyAuthentication() -> identifies auth type +AuthenticationSpec subclass -> applies respective scheme +RequestBuilder.addHeader() or addQueryParam() +``` + +--- + +## 4. REQUEST BODY HANDLING + +**Spec Definition** (v0.5): +Five distinct request body types with nested structures for multipart: + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Detailed Implementation**: + +### 4.1 JSON Body +```yaml +body: + type: json + data: {...} or [...] or "string" +``` +**Implementation**: Jackson ObjectMapper serializes to JSON bytes, sets Content-Type: application/json + +### 4.2 Text Body +```yaml +body: + type: text # or xml, sparql + data: "string content" +``` +**Implementation**: Sends raw string with appropriate Content-Type (text/plain, application/xml, application/sparql-query) + +### 4.3 Form URL-Encoded Body +```yaml +body: + type: formUrlEncoded + data: {...} or "raw=string&form=data" +``` +**Implementation**: Encodes key-value pairs or raw string, sets Content-Type: application/x-www-form-urlencoded + +### 4.4 Multipart Form Body +```yaml +body: + type: multipartForm + data: + - name: field1 + value: value1 + - name: file + filename: data.txt + value: file content + contentType: text/plain +``` +**Implementation**: Builds multipart/form-data with proper boundary, handles both text and binary parts + +### 4.5 Raw Body +```yaml +body: "raw string payload" +``` +**Implementation**: Sends string as-is, Content-Type depends on context + +**Code Location**: [engine/consumes/HttpClientAdapter.java](engine/consumes/HttpClientAdapter.java) - buildRequestBody() method + +--- + +## 5. SERIALIZATION & DESERIALIZATION FORMATS + +**Spec Definition** (v0.5): +Output raw formats: json, xml, avro, protobuf, csv, yaml + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Conversion Pipeline**: +``` +HTTP Response -> Converter.convertToJson() -> JsonNode -> JsonPath extraction +``` + +**Implementation Details** in [engine/Converter.java](engine/Converter.java): + +| Format | Library | Status | Notes | +|--------|---------|--------|-------| +| **JSON** | Jackson ObjectMapper | ✅ Full | Default, native support | +| **XML** | Jackson XmlMapper | ✅ Full | XSD structure preserved, converted to JSON | +| **YAML** | Jackson YAMLFactory | ✅ Full | Complete YAML syntax support | +| **CSV** | Jackson CsvMapper | ✅ Full | Headered CSV to JSON array conversion | +| **Avro** | Jackson AvroMapper + Avro library | ✅ Full | Requires schema in operation spec | +| **Protobuf** | Jackson ProtobufMapper + Protobuf library | ✅ Full | Requires schema in operation spec | + +**Code Examples**: +```java +// Format-specific conversion: +Converter.convertXmlToJson(Reader) -> XmlMapper +Converter.convertCsvToJson(Reader) -> CsvMapper with schema detection +Converter.convertAvroToJson(InputStream, schema) -> DatumReader +Converter.convertProtobufToJson(InputStream, schema) -> ProtobufMapper +``` + +**JsonPath Extraction**: +After conversion to JSON, [engine/Converter.java](engine/Converter.java) uses JayWay JsonPath (com.jayway.jsonpath): +- Supports complex paths: `$.results[*].id`, `$.users[?(@.active==true)].email` +- Maintains type information through extraction + +--- + +## 6. OPERATION FEATURES + +### 6.1 Simple Mode Operations + +**Spec Definition**: +```yaml +operations: + - method: POST + name: create-user + call: external.create-user # Single call to consumed operation + with: + email: "{{email_param}}" + outputParameters: + - name: user_id + type: string + mapping: $.id +``` + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Execution Flow**: +1. [engine/exposes/ApiResourceRestlet.java](engine/exposes/ApiResourceRestlet.java) - handleFromOperationSpec() +2. Resolves input parameters from request +3. Applies "with" parameter injection +4. Finds consumed operation via namespace.operationName +5. Constructs HTTP request +6. Maps response via output parameters + +--- + +### 6.2 Orchestrated Mode Operations (Multi-Step) + +**Spec Definition**: +```yaml +operations: + - method: POST + name: complex-flow + steps: + - type: call + name: fetch-user + call: users.get-user + with: + id: "{{user_id}}" + - type: call + name: fetch-posts + call: posts.get-posts + with: + user_id: "{{fetch-user.id}}" + - type: lookup + name: find-latest + index: fetch-posts + match: timestamp + lookupValue: "$.latest" + outputParameters: [title, content] + mappings: + - targetName: user_data + value: "$.fetch-user" + - targetName: posts_list + value: "$.fetch-posts" + outputParameters: + - name: user_data + type: object + - name: posts_list + type: array +``` + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Core Components**: + +#### 6.2.1 Step Execution +- **Location**: [engine/exposes/OperationStepExecutor.java](engine/exposes/OperationStepExecutor.java) +- **Method**: executeSteps(List, Map baseParameters) + +**Features**: +- Sequential step execution +- Template resolution with {{variable}} syntax +- Parameter merging across steps (baseParameters => step-level with) +- JsonPath support in parameter references +- Error propagation + +#### 6.2.2 Call Steps +- **Location**: [spec/exposes/OperationStepCallSpec.java](spec/exposes/OperationStepCallSpec.java) +- **Execution**: findClientRequestFor() method finds operation, builds request, executes +- **Output Storage**: StepExecutionContext stores JSON output under step name + +**Example Flow**: +```java +Step "fetch-user" executes -> output stored as JSON +Step "fetch-posts" with: {user_id: "{{fetch-user.id}}"} + -> Resolver replaces {{fetch-user.id}} with actual value + -> executes with resolved parameters +``` + +#### 6.2.3 Lookup Steps +- **Location**: [spec/exposes/OperationStepLookupSpec.java](spec/exposes/OperationStepLookupSpec.java) +- **Executor**: [engine/LookupExecutor.java](engine/LookupExecutor.java) +- **Function**: Cross-reference matching within previous step output + +**Lookup Mechanics**: +```yaml +- type: lookup + name: find-matching-post + index: fetch-posts # Reference to "fetch-posts" step output + match: user_id # Match this field in array + lookupValue: "{{user_id}}" # Value to match against + outputParameters: [title, content] # Extract these fields +``` + +**Implementation**: +1. Retrieves index data from StepExecutionContext +2. Finds array items where field matches lookupValue +3. Extracts specified outputParameters +4. Returns as new JSON object +5. Stores in StepExecutionContext under step name + +**Code Example**: +```java +// LookupExecutor.executeLookup() +List matches = findMatchingItems(indexData, matchField, lookupValue); +JsonNode result = extractFields(matches, outputParameters); +stepContext.storeStepOutput(stepName, result); +``` + +--- + +### 6.3 Step Output Mapping + +**Spec Definition**: +```yaml +mappings: + - targetName: user_data + value: "$.fetch-user" + - targetName: combined_results + value: "$.fetch-posts[*].id" # Can use JsonPath +``` + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Location**: [spec/exposes/StepOutputMapping.java](spec/exposes/StepOutputMapping.java) + +**Features**: +- Maps step outputs to operation output parameters +- Supports JsonPath expressions for nested/array access +- Executes after all steps complete +- Required for orchestrated operations with named output parameters + +--- + +### 6.4 Output Parameter Structures + +**Spec Definition**: Two modes depending on operation type + +#### Simple Mode - MappedOutputParameter +```yaml +outputParameters: + - name: user_id + type: string + mapping: $.id + - name: is_active + type: boolean + mapping: $.active + - name: tags + type: array + mapping: $.tags + - name: metadata + type: object + mapping: $.meta + properties: + created_at: + type: string + mapping: $.createdAt + updated_at: + type: string + mapping: $.updatedAt +``` + +#### Orchestrated Mode - OrchestratedOutputParameter +```yaml +outputParameters: + - name: users + type: array + items: + - name: id + type: string + - name: email + type: string + - name: total_count + type: number + - name: metadata + type: object + properties: + timestamp: + type: string + status: + type: string +``` + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Deserialization**: [spec/OutputParameterDeserializer.java](spec/OutputParameterDeserializer.java) +- Handles polymorphic type detection (string, number, boolean, object, array) +- Recursively deserializes nested structures +- Supports both const values and mapping expressions + +--- + +## 7. INPUT PARAMETERS + +**Spec Definition**: +Input parameters available in path, query, header, cookie, body, environment locations + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**For Exposed API/MCP**: +```java +// From ExposedInputParameter spec +- name: user_id + in: path + type: string + description: "User identifier" + pattern: "^[a-z0-9-]+$" // Regex validation + value: "{{user_id}}" // Can bind to variable +``` + +**For Consumed HTTP**: +```java +// From ConsumedInputParameter spec +- name: Authorization + in: header + value: "Bearer {{api_key}}" +``` + +**Implementation**: +- **Location**: [engine/Resolver.java](engine/Resolver.java) - resolveInputParameter() +- **Locations Handled**: query, header, path, cookie, body, environment +- **Features**: + - Template resolution (Mustache) + - JsonPath extraction from body + - Environment variable interpolation + - Type validation + +--- + +## 8. EXTERNAL REFERENCES + +**Spec Definition** (v0.5): +Two types of external references for variable injection + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +### 8.1 File-Resolved References +```yaml +externalRefs: + - name: database-creds + type: environment + resolution: file + uri: /etc/naftiko/db-secrets.json + keys: + db_user: DB_USERNAME + db_pass: DB_PASSWORD +``` + +**Implementation**: [engine/ExternalRefResolver.java](engine/ExternalRefResolver.java) +- Loads JSON file at capability startup +- Extracts specified keys +- Makes available as {{db_user}}, {{db_pass}} in templates + +### 8.2 Runtime-Resolved References +```yaml +externalRefs: + - name: env-vars + type: variables + resolution: runtime + keys: + api_key: NOTION_API_KEY + workspace: WORKSPACE_ID +``` + +**Implementation**: [engine/ExternalRefResolver.java](engine/ExternalRefResolver.java) +- Resolves at runtime from execution context +- Looks up environment variables +- Makes available as {{api_key}}, {{workspace}} + +**Code Location**: [Capability.java](Capability.java) - constructor calls ExternalRefResolver + +--- + +## 9. FORWARD CONFIGURATION + +**Spec Definition**: +```yaml +resources: + - path: "/proxy/**" + forward: + targetNamespace: external-api + trustedHeaders: + - Authorization + - X-Custom-Header +``` + +**Implementation Status**: ✅ **FULLY IMPLEMENTED** + +**Functionality**: +- Forwards incoming requests to a consumed HTTP operation +- Allows selective header forwarding (allowlist) +- Useful for API gateway/proxy patterns + +**Implementation**: [engine/exposes/ApiResourceRestlet.java](engine/exposes/ApiResourceRestlet.java) +- Method: handleFromForwardSpec() +- Finds target consumer namespace +- Copies specified headers +- Executes forward request +- Returns response directly + +**Extension**: New "ForwardValue" feature allows dynamic path/parameter modification: +- [spec/exposes/ApiServerForwardSpec.java](spec/exposes/ApiServerForwardSpec.java) +- Supports Mustache template resolution in forward parameters + +--- + +## 10. GAPS & MISSING FEATURES + +### 10.1 Conditional Logic (NOT IN v0.5 SPEC) + +**Status**: ❌ **NOT IMPLEMENTED** (intentionally - not in v0.5 spec) + +**Historical Note**: Earlier versions (v0.2) had if/then/else in JSON schema but this was removed in v0.5. + +**What's Missing**: +- No if/then/else conditional branching in steps +- No switch/case routing +- No conditional mappings +- No expression evaluation (boolean, comparison operators) + +**Impact**: Multi-step flows must execute all steps sequentially; cannot dynamically skip or route based on conditions. + +**Example of Missing Feature**: +```yaml +# This is NOT supported: +steps: + - type: call + name: check-status + call: external.get-status + - type: conditional + condition: "{{check-status.is_active}} == true" + if_true: + - type: call + name: process + call: external.process + if_false: + - type: call + name: notify + call: external.notify-inactive +``` + +--- + +### 10.2 Error Handling & Recovery + +**Status**: ⚠️ **BASIC IMPLEMENTATION ONLY** + +**Current Implementation**: +- Exception throwing and logging (Restlet logger) +- HTTP error status codes (400, 500) +- No retry mechanisms +- No circuit breaker patterns +- No fallback chains +- No timeout configuration + +**Missing**: +- Retry with exponential backoff +- Circuit breaker for failing services +- Fallback steps in orchestration +- Timeout specifications +- Per-operation error handlers +- Error aggregation for multi-step flows + +**Current Code**: +- [engine/exposes/OperationStepExecutor.java](engine/exposes/OperationStepExecutor.java) - throws RuntimeException on step failure +- [engine/exposes/ApiResourceRestlet.java](engine/exposes/ApiResourceRestlet.java) - catches and logs, returns error status + +**Impact**: If any step in orchestration fails, entire operation fails with no recovery. + +--- + +### 10.3 Async & Parallel Execution + +**Status**: ❌ **NOT IMPLEMENTED** + +**Current Behavior**: All operations are synchronous, blocking +- Step execution is sequential (step N waits for step N-1) +- No parallel step execution +- No async/await patterns +- No background job handling +- No long-running operation support + +**What's Missing**: +```yaml +# This is NOT supported: +steps: + - type: call + name: fetch-user # Wait for completion + call: users.get + - type: parallel # Run simultaneously + steps: + - type: call + name: fetch-posts + call: posts.list + - type: call + name: fetch-comments + call: comments.list + - type: call + name: aggregate # Wait for parallel to complete + call: utils.merge + with: + posts: "{{fetch-posts}}" + comments: "{{fetch-comments}}" +``` + +**Impact**: Cannot parallelize independent operations; overall latency = sum of all step latencies + +--- + +### 10.4 Caching & Response Memoization + +**Status**: ❌ **NOT IMPLEMENTED** + +**Missing**: +- Response caching (in-memory, Redis, etc.) +- Cache TTL configuration +- Cache invalidation strategies +- ETag support +- Conditional request optimization + +**Impact**: Every operation invocation hits the source system; no deduplication or response reuse + +--- + +### 10.5 Rate Limiting & Throttling + +**Status**: ❌ **NOT IMPLEMENTED** + +**Missing**: +- Per-operation rate limits +- Sliding window rate limiting +- Backpressure handling +- Token bucket strategies +- Per-client limiting +- Per-API-key limiting + +**Impact**: No protection against overwhelming consumed services or overwhelming exposed API + +--- + +### 10.6 Logging & Monitoring + +**Status**: ⚠️ **BASIC IMPLEMENTATION** + +**Current**: +- Restlet framework logging (java.util.logging) +- Basic exception logging +- Test frameworks for integration testing + +**Missing**: +- Structured logging (JSON logs) +- Distributed tracing (OpenTelemetry, Jaeger) +- Metrics collection (Prometheus, Micrometer) +- Audit logging +- Request/response logging +- Performance metrics per operation + +--- + +### 10.7 Input Validation + +**Status**: ⚠️ **PARTIAL IMPLEMENTATION** + +**Current**: +- Type checking (string, number, boolean, array, object) +- Regex pattern validation for string parameters +- Required vs optional parameters + +**Missing**: +- Not-null constraints +- Min/max value validation +- Array length validation +- Custom validators +- Comprehensive error messages + +--- + +### 10.8 Security Features + +**Status**: ✅ **AUTHENTICATION ONLY** + +**Implemented**: +- Authentication (Basic, Bearer, ApiKey, Digest) +- HTTPS support (Jetty/Restlet) +- Header filtering (forward config whitelist) + +**Missing**: +- CORS handling +- CSRF protection +- Rate limiting for DDoS prevention +- Input sanitization (SQL injection, XSS) +- Schema validation +- SSL certificate validation configuration +- API key rotation +- Access token expiration/refresh + +--- + +### 10.9 Data Transformation & Normalization + +**Status**: ⚠️ **PARTIAL IMPLEMENTATION** + +**Current**: +- JsonPath extraction (read-only) +- Format conversion (XML/CSV/Avro/Protobuf to JSON) +- Mapping to output parameters + +**Missing**: +- Custom transformation functions +- Data normalization (trim, lowercase, etc.) +- field renaming/aliasing +- Calculated fields +- Aggregation functions (sum, count, etc.) +- Date/time formatting +- Number formatting + +--- + +### 10.10 Schema Evolution & Versioning + +**Status**: ❌ **NOT IMPLEMENTED** + +**Missing**: +- Schema versioning +- Backward/forward compatibility checking +- Schema migration strategies +- Deprecation markers +- Version negotiation + +--- + +## 11. IMPLEMENTATION STRENGTH AREAS + +### 11.1 Exposition Flexibility +- Multiple exposure patterns: REST API + MCP HTTP + MCP Stdio +- Single capability supports multiple exposure modes simultaneously +- Standardized request/response handling across transports + +### 11.2 Serialization Support +- 6 output formats with complete conversion pipeline +- Proper use of Jackson ecosystem (ObjectMapper, XmlMapper, CsvMapper, AvroMapper, ProtobufMapper) +- JsonPath for complex data extraction + +### 11.3 Orchestration +- Clean separation of concerns (OperationStepExecutor for shared logic) +- Proper step context management (StepExecutionContext) +- Both call and lookup steps with cross-referencing +- Template resolution throughout pipeline + +### 11.4 Authentication +- Comprehensive authentication type support +- Correct implementation of auth schemes (Basic, Bearer, Digest) +- Clean polymorphic design (AuthenticationSpec hierarchy) + +### 11.5 Parameter Management +- Flexible parameter locations (6 input locations supported) +- Template resolution with Mustache syntax +- Type-safe parameter handling +- Environment variable injection + +--- + +## 12. RECOMMENDATIONS FOR CLOSING GAPS + +### High Priority (Business Impact) +1. **Add Conditional Logic** - Enable if/then/else branching in orchestration +2. **Implement Error Recovery** - Retry mechanisms, fallback steps, error aggregation +3. **Add Async Support** - Parallel step execution, background jobs + +### Medium Priority (Operational) +4. **Enhance Logging** - Structured logging, distributed tracing support +5. **Add Monitoring** - Metrics collection, performance instrumentation +6. **Improve Error Messages** - More descriptive validation errors + +### Low Priority (Nice to Have) +7. **Caching & Memoization** - Response caching layer +8. **Rate Limiting** - Per-operation, per-client throttling +9. **Data Transformation** - Custom transformation functions + +--- + +## 13. TESTING COVERAGE ANALYSIS + +### Formats Tested +- ✅ Avro format (CapabilityAvroIntegrationTest) +- ✅ CSV format (CapabilityCsvIntegrationTest) +- ✅ XML format (CapabilityXmlIntegrationTest) +- ✅ YAML format (implicit in schema loading) +- ✅ Protobuf format (CapabilityProtobufIntegrationTest) +- ✅ JSON format (default, extensively tested) + +### Features Tested +- ✅ API Authentication (CapabilityApiAuthenticationIntegrationTest) +- ✅ MCP HTTP (CapabilityMcpIntegrationTest) +- ✅ MCP Stdio (CapabilityMcpStdioIntegrationTest) +- ✅ Forward Header (CapabilityForwardHeaderIntegrationTest) +- ✅ Forward Value Field (CapabilityForwardValueFieldTest) +- ✅ HTTP Body handling (CapabilityHttpBodyIntegrationTest) +- ✅ Query & Header parameters (CapabilityHeaderQueryIntegrationTest) +- ✅ Output mappings (OutputMappingExtensionTest) + +### Not Explicitly Tested +- ❌ Error recovery scenarios +- ❌ Timeout handling +- ❌ Performance under load +- ❌ Concurrent multi-step orchestrations + +--- + +## 14. CONCLUSION + +The Naftiko framework v0.5 delivers **strong core functionality** with excellent support for: +- Multiple exposition patterns +- Comprehensive consumption of HTTP APIs +- Full authentication support +- Advanced multi-step orchestration with lookups +- Rich data format support + +**Primary gaps** are in advanced operational features (error recovery, monitoring, async execution) rather than core specification compliance. These gaps are **intentional design choices** (async explicitly not prioritized) or **future enhancements** (monitoring, caching). + +The framework is **production-ready for basic to intermediate use cases** and can be extended to support advanced scenarios by implementing the recommended gap-closure items. + diff --git a/src/main/resources/blueprints/mcp-resources-prompts-proposal.md b/src/main/resources/blueprints/mcp-resources-prompts-proposal.md new file mode 100644 index 0000000..47a6cdb --- /dev/null +++ b/src/main/resources/blueprints/mcp-resources-prompts-proposal.md @@ -0,0 +1,1330 @@ +# MCP Resources & Prompt Templates Support Proposal +## Extending the MCP Server Adapter with Resources and Prompts + +**Status**: Proposal +**Date**: March 5, 2026 +**Key Concept**: Add MCP resources and prompt templates to the existing `mcp` server adapter, aligning with the MCP specification while maintaining consistency with the existing `api` adapter patterns and the Agent Skills proposal's `location`-based file serving. + +--- + +## Table of Contents + +1. [Executive Summary](#executive-summary) +2. [Architecture Overview](#architecture-overview) +3. [Design Analogy](#design-analogy) +4. [MCP Resources](#mcp-resources) +5. [MCP Prompt Templates](#mcp-prompt-templates) +6. [Schema Amendments](#schema-amendments) +7. [Protocol Changes](#protocol-changes) +8. [Implementation Examples](#implementation-examples) +9. [Security Considerations](#security-considerations) +10. [Validation Rules](#validation-rules) +11. [Implementation Roadmap](#implementation-roadmap) +12. [Backward Compatibility](#backward-compatibility) + +--- + +## Executive Summary + +### What This Proposes + +Extend the current `mcp` server adapter — which today only supports **tools** — with two additional MCP primitives: + +1. **Resources** — Expose data and content that agents can read. Two source types: + - **Dynamic** (`call`/`steps`): Resources backed by consumed HTTP operations, using the same orchestration model as tools + - **Static** (`location`): Resources served from local files, aligned with the Agent Skills proposal's `location`-based file serving pattern + +2. **Prompt Templates** — Expose reusable prompt templates with typed arguments that agents can discover and render. Two source types: + - **Inline** (`template`): Prompt content declared directly in YAML + - **File-based** (`location`): Prompt content loaded from a local file + +### Why Extend the MCP Adapter? + +The [MCP specification](https://spec.modelcontextprotocol.io/) defines three core server primitives: + +| Primitive | Purpose | Current Support | +|-----------|---------|-----------------| +| **Tools** | Model-controlled functions agents can invoke | **Supported** | +| **Resources** | Application-controlled data agents can read | **Not supported** | +| **Prompts** | User-controlled templates agents can render | **Not supported** | + +Supporting all three primitives makes Naftiko a complete MCP server implementation. Resources and prompts are purely additive — they do not change tool execution. + +### Business Value + +| Benefit | Impact | Users | +|---------|--------|-------| +| **Complete MCP compliance** | Full server primitive coverage (tools + resources + prompts) | Developers | +| **Data exposure** | Expose configuration, documentation, and API responses as readable resources | AI Agents | +| **Prompt standardization** | Distribute reusable prompt templates through MCP protocol | Prompt Engineers | +| **File serving** | Serve local files as MCP resources, consistent with the `skill` adapter's `location` pattern | Organizations | +| **Agent context** | Agents read resources for context before invoking tools | AI Applications | + +### Key Design Decisions + +1. **Same orchestration model**: Dynamic resources use `call`/`steps`/`with` exactly like tools — no new execution paradigm. Agents already understand this pattern. + +2. **Static resources from local files**: The `location` property uses a `file:///` URI pointing to a directory, consistent with `ExposedSkill.location` in the Agent Skills proposal. Files under that directory are served as individual MCP resources. + +3. **Prompt templates are declarative**: Prompts declare arguments and content — the MCP server renders them. No orchestration needed. + +4. **MCP protocol methods**: New JSON-RPC methods (`resources/list`, `resources/read`, `resources/templates/list`, `prompts/list`, `prompts/get`) follow the MCP specification exactly. + +5. **Capability advertisement**: The `initialize` response advertises `resources` and/or `prompts` capabilities only when they are declared in the spec. + +6. **Tools remain required for now**: The `tools` array remains required on `ExposesMcp`. A future schema revision may relax this to allow resource-only or prompt-only MCP servers. + +### Risk Assessment + +| Risk | Likelihood | Impact | Mitigation | +|------|-----------|--------|-----------| +| **Path traversal** (static resources) | Medium | High | Strict path validation, resolved path containment check | +| **Large file serving** | Low | Medium | Size limits, streaming | +| **Schema complexity** | Low | Low | Additive — new optional arrays alongside existing `tools` | +| **MCP version drift** | Low | Medium | Pin to MCP protocol version `2025-03-26` | + +**Overall Risk**: **LOW** — Purely additive; tools behavior unchanged + +--- + +## Architecture Overview + +### Current State + +``` +ExposesMcp +├── type: "mcp" +├── transport: "http" | "stdio" +├── namespace +├── description +└── tools[] ← only primitive today + ├── name, label, description + ├── inputParameters[] + ├── call / steps / with + └── outputParameters[] +``` + +### Proposed State + +``` +ExposesMcp +├── type: "mcp" +├── transport: "http" | "stdio" +├── namespace +├── description +├── tools[] ← unchanged +│ ├── name, label, description +│ ├── inputParameters[] +│ ├── call / steps / with +│ └── outputParameters[] +├── resources[] ← NEW +│ ├── name, label, uri, description, mimeType +│ ├── Dynamic: call / steps / with +│ └── Static: location +└── prompts[] ← NEW + ├── name, label, description + ├── arguments[] + └── template / location +``` + +--- + +## Design Analogy + +### How the three primitives relate across adapters + +``` +API Adapter MCP Adapter (current) MCP Adapter (proposed) +───────────── ───────────────────── ────────────────────── +ExposesApi ExposesMcp ExposesMcp +├─ resources[] ├─ tools[] ├─ tools[] +│ ├─ path │ ├─ name │ ├─ name +│ ├─ description │ ├─ label │ ├─ label +│ ├─ operations[] │ ├─ description │ ├─ description +│ │ ├─ inputParameters[] │ ├─ inputParameters[] +│ │ ├─ method │ ├─ call / steps │ ├─ call / steps +│ │ ├─ call / steps │ ├─ with │ ├─ with +│ │ └─ outputParameters[] │ └─ outputParameters[] │ └─ outputParameters[] +│ └─ forward │ │ +│ │ ├─ resources[] ← NEW +│ │ │ ├─ name, label, uri +│ │ │ ├─ description +│ │ │ ├─ mimeType +│ │ │ ├─ call / steps (dynamic) +│ │ │ └─ location (static) +│ │ │ +│ │ └─ prompts[] ← NEW +│ │ ├─ name, label, description +│ │ ├─ arguments[] +│ │ └─ template / location +``` + +### Conceptual mapping: API adapter ↔ MCP adapter + +| API Adapter concept | MCP Tool (existing) | MCP Resource (new) | MCP Prompt (new) | +|---------------------|--------------------:|-------------------:|------------------:| +| Resource path | Tool name | Resource URI | Prompt name | +| Operation (GET/POST) | `call`/`steps` | `call`/`steps` or `location` | `template`/`location` | +| inputParameters | inputParameters | — (resources are parameterless in MCP) | arguments | +| outputParameters | outputParameters | Content (text/blob) | Messages | +| Forward | — | Static `location` | File-based `location` | + +--- + +## MCP Resources + +MCP resources expose data that agents can **read** (but not invoke like tools). Resources are identified by URI and return typed content. + +### Two Source Types + +#### 1. Dynamic Resources (`call`/`steps`) + +Dynamic resources are backed by consumed HTTP operations. They use the same orchestration model as tools: + +```yaml +resources: + - name: "current-config" + label: "Current Configuration" + uri: "config://app/current" + description: "Current application configuration" + mimeType: "application/json" + call: "config-api.get-config" +``` + +When an agent reads this resource, the MCP server executes the consumed operation and returns the response as resource content. + +**With steps (orchestrated):** + +```yaml +resources: + - name: "user-summary" + label: "User Summary" + uri: "data://users/summary" + description: "Aggregated user summary from multiple API calls" + mimeType: "application/json" + steps: + - type: "call" + name: "fetch-users" + call: "user-api.list-users" + - type: "call" + name: "fetch-stats" + call: "analytics-api.get-stats" + mappings: + - targetName: users + value: "{{$.fetch-users.data}}" + - targetName: stats + value: "{{$.fetch-stats.summary}}" + outputParameters: + - name: users + type: array + - name: stats + type: object +``` + +#### 2. Static Resources (`location`) + +Static resources are served from local files. The `location` property is a `file:///` URI pointing to a directory — consistent with the `location` pattern in the Agent Skills proposal's `ExposedSkill`: + +```yaml +resources: + - name: "api-docs" + label: "API Documentation" + uri: "docs://api/reference" + description: "API reference documentation" + mimeType: "text/markdown" + location: "file:///etc/naftiko/resources/api-docs" +``` + +**Expected directory structure at the location:** +``` +/etc/naftiko/resources/api-docs/ +├── index.md +├── endpoints/ +│ ├── users.md +│ └── orders.md +└── schemas/ + └── response.json +``` + +Each file under the location directory becomes a separate MCP resource. The server auto-generates URIs based on the resource's `uri` prefix and relative file paths: + +| File | Generated MCP Resource URI | +|------|---------------------------| +| `index.md` | `docs://api/reference/index.md` | +| `endpoints/users.md` | `docs://api/reference/endpoints/users.md` | +| `schemas/response.json` | `docs://api/reference/schemas/response.json` | + +If a `location` is specified without sub-files, the directory itself is the resource and the `uri` resolves directly to its content. + +### Resource URI Schemes + +MCP resources use URIs to identify content. The URI is declared by the capability author: + +```yaml +# Custom scheme (recommended for clarity) +uri: "config://app/current" +uri: "docs://api/reference" +uri: "data://users/summary" + +# HTTPS scheme (for resources that mirror external URLs) +uri: "https://api.example.com/config" +``` + +The URI is an identifier — it does not imply how the resource is fetched. Dynamic resources execute consumed operations; static resources read local files. + +### Resource Template URIs + +For dynamic resources, the URI can contain parameters using the `{param}` placeholder syntax (consistent with the MCP spec's resource templates): + +```yaml +resources: + - name: "user-profile" + uri: "data://users/{userId}/profile" + description: "User profile by ID" + mimeType: "application/json" + call: "user-api.get-user" + with: + user_id: "{{userId}}" +``` + +Resource templates are advertised via `resources/templates/list` and resolved when agents call `resources/read` with a concrete URI. + +--- + +## MCP Prompt Templates + +MCP prompts are reusable templates with typed arguments that agents can discover and render into structured messages. + +### Two Source Types + +#### 1. Inline Prompts (`template`) + +Prompt content declared directly in YAML. Arguments are injected via `{{arg}}` placeholders: + +```yaml +prompts: + - name: "summarize-data" + label: "Summarize Data" + description: "Summarize API response data for the user" + arguments: + - name: "data" + description: "The raw API response data to summarize" + required: true + - name: "format" + description: "Desired output format (bullet-points, paragraph, table)" + required: false + template: + - role: "user" + content: "Summarize the following data in {{format}} format:\n\n{{data}}" +``` + +#### 2. File-Based Prompts (`location`) + +Prompt content loaded from a local file. Consistent with the `location` pattern used by static resources and the Agent Skills proposal: + +```yaml +prompts: + - name: "code-review" + description: "Structured code review prompt with context" + arguments: + - name: "language" + description: "Programming language" + required: true + - name: "code" + description: "Code to review" + required: true + location: "file:///etc/naftiko/prompts/code-review.md" +``` + +The file at the location contains the prompt template with `{{arg}}` placeholders. The MCP server reads the file, substitutes arguments, and returns the rendered messages. + +**File content (`code-review.md`):** +```markdown +Review the following {{language}} code for: +- Correctness +- Performance +- Security +- Readability + +```{{language}} +{{code}} +``` + +Provide specific, actionable feedback. +``` + +When a file-based prompt is rendered, its content becomes a single `user` role message by default. + +### Prompt Arguments + +Arguments are typed parameters that agents provide when rendering a prompt: + +```yaml +arguments: + - name: "topic" + description: "The topic to analyze" + required: true + - name: "depth" + description: "Analysis depth: brief, standard, or deep" + required: false +``` + +Arguments follow the same conventions as `McpToolInputParameter` but are simpler — they only have `name`, `description`, and `required`. No `type` field is needed because prompt arguments are always strings (per MCP spec). + +### Prompt Messages + +Inline prompts declare messages as an array of `{role, content}` objects. The `role` must be one of `"user"` or `"assistant"`: + +```yaml +template: + - role: "user" + content: "You are an expert in {{domain}}. Analyze the following:\n\n{{input}}" + - role: "assistant" + content: "I'll analyze this from the perspective of {{domain}}. Let me examine the key aspects." + - role: "user" + content: "Focus specifically on: {{focus_area}}" +``` + +--- + +## Schema Amendments + +### Amendment 1: Update `ExposesMcp` — Add `resources` and `prompts` + +Add two optional arrays to the existing `ExposesMcp` definition: + +```json +{ + "ExposesMcp": { + "type": "object", + "description": "MCP Server exposition configuration. Exposes tools, resources and prompts over MCP transport (Streamable HTTP or stdio).", + "properties": { + "type": { + "type": "string", + "const": "mcp" + }, + "transport": { + "type": "string", + "enum": ["http", "stdio"], + "default": "http", + "description": "The MCP transport to use. 'http' (default) exposes a Streamable HTTP server; 'stdio' uses stdin/stdout JSON-RPC for local IDE integration." + }, + "address": { + "$ref": "#/$defs/Address" + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "namespace": { + "$ref": "#/$defs/IdentifierKebab", + "description": "Unique identifier for this exposed MCP server" + }, + "description": { + "type": "string", + "description": "A meaningful description of this MCP server's purpose. Used as the server instructions sent during MCP initialization." + }, + "tools": { + "type": "array", + "description": "List of MCP tools exposed by this server", + "items": { + "$ref": "#/$defs/McpTool" + }, + "minItems": 1 + }, + "resources": { + "type": "array", + "description": "List of MCP resources exposed by this server. Resources provide data that agents can read.", + "items": { + "$ref": "#/$defs/McpResource" + }, + "minItems": 1 + }, + "prompts": { + "type": "array", + "description": "List of MCP prompt templates exposed by this server. Prompts are reusable templates with typed arguments.", + "items": { + "$ref": "#/$defs/McpPrompt" + }, + "minItems": 1 + } + }, + "required": [ + "type", + "namespace", + "tools" + ], + "oneOf": [ + { + "properties": { + "transport": { "const": "stdio" } + }, + "required": ["transport"], + "not": { "required": ["port"] } + }, + { + "properties": { + "transport": { "const": "http" } + }, + "required": ["port"] + } + ], + "additionalProperties": false + } +} +``` + +**Changes from current schema:** +- Description updated to mention resources and prompts +- `McpTool` should include a required `label` for human-readable display name +- Added `resources` (optional array of `McpResource`) +- Added `prompts` (optional array of `McpPrompt`) +- `tools` remains required (future revision may relax this) +- Transport rules unchanged + +### Amendment 1b: Update `McpTool` — Add `label` + +Extend `McpTool` with a display label that maps to MCP descriptor `title`: + +```json +{ + "McpTool": { + "type": "object", + "properties": { + "name": { + "$ref": "#/$defs/IdentifierExtended", + "description": "Technical name for the tool. Used as identifier in MCP tool calls." + }, + "label": { + "type": "string", + "description": "Human-readable display name of the tool. Mapped to MCP 'title' in tools/list responses." + }, + "description": { + "type": "string", + "description": "A meaningful description of the tool and when to use it. Used for agent discovery." + } + }, + "required": ["name", "label", "description"] + } +} +``` + +--- + +### Amendment 2: New `McpResource` Definition + +```json +{ + "McpResource": { + "type": "object", + "description": "An MCP resource definition. Exposes data that agents can read. Either dynamic (backed by consumed HTTP operations via call/steps) or static (served from local files via location).", + "properties": { + "name": { + "$ref": "#/$defs/IdentifierExtended", + "description": "Technical name for the resource. Used as identifier in MCP resource listings." + }, + "label": { + "type": "string", + "description": "Human-readable display name of the resource. Mapped to MCP 'title' in protocol responses." + }, + "uri": { + "type": "string", + "description": "The URI that identifies this resource in MCP. Can use any scheme (e.g. config://, docs://, data://). For resource templates, use {param} placeholders." + }, + "description": { + "type": "string", + "description": "A meaningful description of the resource. Used for agent discovery. In a world of agents, context is king." + }, + "mimeType": { + "type": "string", + "description": "MIME type of the resource content per RFC 6838 (e.g. application/json, text/markdown, text/plain). Optional parameters are supported (e.g. charset=utf-8).", + "pattern": "^[a-zA-Z0-9!#$&^_.+-]+\\/[a-zA-Z0-9!#$&^_.+-]+(?:\\s*;\\s*[a-zA-Z0-9!#$&^_.+-]+=(?:[a-zA-Z0-9!#$&^_.+-]+|\\\"[^\\\"]*\\\"))*$", + "examples": [ + "application/json", + "text/markdown; charset=utf-8", + "application/vnd.api+json" + ] + }, + "call": { + "type": "string", + "description": "For dynamic resources: reference to the consumed operation that produces the resource content. Format: {namespace}.{operationId}.", + "pattern": "^[a-zA-Z0-9-]+\\.[a-zA-Z0-9-]+$" + }, + "with": { + "$ref": "#/$defs/WithInjector" + }, + "steps": { + "type": "array", + "items": { + "$ref": "#/$defs/OperationStep" + }, + "minItems": 1 + }, + "mappings": { + "type": "array", + "description": "Maps step outputs to the resource content.", + "items": { + "$ref": "#/$defs/StepOutputMapping" + } + }, + "outputParameters": { + "type": "array" + }, + "location": { + "type": "string", + "format": "uri", + "pattern": "^file:///", + "description": "For static resources: file:/// URI pointing to a directory whose files are served as MCP resources. Consistent with ExposedSkill.location." + } + }, + "required": [ + "name", + "label", + "uri", + "description" + ], + "oneOf": [ + { + "required": ["call"], + "type": "object", + "properties": { + "outputParameters": { + "type": "array", + "items": { + "$ref": "#/$defs/MappedOutputParameter" + } + } + }, + "not": { "required": ["location"] } + }, + { + "required": ["steps"], + "type": "object", + "properties": { + "mappings": true, + "outputParameters": { + "type": "array", + "items": { + "$ref": "#/$defs/OrchestratedOutputParameter" + } + } + }, + "not": { "required": ["location"] } + }, + { + "required": ["location"], + "not": { + "anyOf": [ + { "required": ["call"] }, + { "required": ["steps"] } + ] + } + } + ], + "additionalProperties": false + } +} +``` + +**Design notes:** +- `name`, `label`, `uri`, `description` are always required +- `label` is author-facing in Naftiko and maps to MCP descriptor `title` +- Exactly one source: `call` (simple dynamic), `steps` (orchestrated dynamic), or `location` (static) +- Dynamic resources reuse `WithInjector`, `OperationStep`, `StepOutputMapping`, `MappedOutputParameter`, and `OrchestratedOutputParameter` — no new execution types +- `location` uses `file:///` URI with the same pattern as `ExposedSkill.location` in the Agent Skills proposal +- `mimeType` is optional — inferred from content or file extension when absent +- Resource template URIs (with `{param}` placeholders) are supported via the `uri` field + +--- + +### Amendment 3: New `McpPrompt` Definition + +```json +{ + "McpPrompt": { + "type": "object", + "description": "An MCP prompt template definition. Prompts are reusable templates with typed arguments that agents can discover and render.", + "properties": { + "name": { + "$ref": "#/$defs/IdentifierExtended", + "description": "Technical name for the prompt. Used as identifier in MCP prompt listings." + }, + "label": { + "type": "string", + "description": "Human-readable display name of the prompt. Mapped to MCP 'title' in protocol responses." + }, + "description": { + "type": "string", + "description": "A meaningful description of the prompt and when to use it. Used for agent discovery." + }, + "arguments": { + "type": "array", + "description": "Typed arguments for this prompt template. Arguments are substituted into the template via {{arg}} placeholders.", + "items": { + "$ref": "#/$defs/McpPromptArgument" + }, + "minItems": 1 + }, + "template": { + "type": "array", + "description": "Inline prompt template as an array of messages. Each message has a role and content with {{arg}} placeholders.", + "items": { + "$ref": "#/$defs/McpPromptMessage" + }, + "minItems": 1 + }, + "location": { + "type": "string", + "format": "uri", + "pattern": "^file:///", + "description": "File-based prompt: file:/// URI pointing to a file containing the prompt template with {{arg}} placeholders. Content becomes a single 'user' message. Consistent with ExposedSkill.location and McpResource.location." + } + }, + "required": [ + "name", + "label", + "description" + ], + "oneOf": [ + { + "required": ["template"], + "not": { "required": ["location"] } + }, + { + "required": ["location"], + "not": { "required": ["template"] } + } + ], + "additionalProperties": false + } +} +``` + +--- + +### Amendment 4: New `McpPromptArgument` Definition + +```json +{ + "McpPromptArgument": { + "type": "object", + "description": "An argument for an MCP prompt template. Arguments are always strings per MCP spec.", + "properties": { + "name": { + "$ref": "#/$defs/IdentifierExtended", + "description": "Argument name. Becomes a {{name}} placeholder in the template." + }, + "label": { + "type": "string", + "description": "The display name of the argument. Used for agent discovery." + }, + "description": { + "type": "string", + "description": "A meaningful description of the argument. Used for agent discovery." + }, + "required": { + "type": "boolean", + "description": "Whether the argument is required. Defaults to true.", + "default": true + } + }, + "required": [ + "name", + "description" + ], + "additionalProperties": false + } +} +``` + +**Design notes:** +- Follows the same pattern as `McpToolInputParameter` but without `type` (prompt arguments are always strings per MCP spec) +- Same `required` field semantics with `default: true` + +--- + +### Amendment 5: New `McpPromptMessage` Definition + +```json +{ + "McpPromptMessage": { + "type": "object", + "description": "A message in an inline MCP prompt template. Supports {{arg}} placeholders for argument substitution.", + "properties": { + "role": { + "type": "string", + "enum": ["user", "assistant"], + "description": "The role of the message sender." + }, + "content": { + "type": "string", + "description": "The message content. Supports {{arg}} placeholders for argument substitution." + } + }, + "required": ["role", "content"], + "additionalProperties": false + } +} +``` + +--- + +## Protocol Changes + +### Updated `initialize` Response + +The `initialize` response must advertise `resources` and/or `prompts` capabilities when they are declared: + +```json +{ + "protocolVersion": "2025-03-26", + "capabilities": { + "tools": {}, + "resources": {}, + "prompts": {} + }, + "serverInfo": { + "name": "weather-mcp", + "version": "1.0.0" + } +} +``` + +Only advertise capabilities that are configured: +- `tools` — always (tools remain required) +- `resources` — only when `resources[]` is non-empty on the spec +- `prompts` — only when `prompts[]` is non-empty on the spec + +### New JSON-RPC Methods + +| Method | Purpose | Request Params | Response | +|--------|---------|----------------|----------| +| `tools/list` | List all tools | — | `{ tools: McpToolDescriptor[] }` | +| `resources/list` | List all resources | — | `{ resources: McpResourceDescriptor[] }` | +| `resources/read` | Read resource content | `{ uri: string }` | `{ contents: [{ uri, mimeType?, text? , blob? }] }` | +| `resources/templates/list` | List resource templates | — | `{ resourceTemplates: McpResourceTemplateDescriptor[] }` | +| `prompts/list` | List all prompts | — | `{ prompts: McpPromptDescriptor[] }` | +| `prompts/get` | Render a prompt | `{ name: string, arguments?: object }` | `{ messages: [{ role, content: { type, text } }] }` | + +### `tools/list` Response + +```json +{ + "tools": [ + { + "name": "get-weather", + "title": "Get Weather", + "description": "Get current weather for a city", + "inputSchema": { + "type": "object" + } + } + ] +} +``` + +### `resources/list` Response + +```json +{ + "resources": [ + { + "uri": "config://app/current", + "name": "current-config", + "title": "Current Configuration", + "description": "Current application configuration", + "mimeType": "application/json" + }, + { + "uri": "docs://api/reference/index.md", + "name": "api-docs", + "title": "API Documentation", + "description": "API reference documentation", + "mimeType": "text/markdown" + } + ] +} +``` + +For static resources with `location`, each file in the directory is listed as a separate resource with an auto-generated URI. + +### `resources/read` Response + +```json +{ + "contents": [ + { + "uri": "config://app/current", + "mimeType": "application/json", + "text": "{\"version\": \"2.1\", \"environment\": \"production\"}" + } + ] +} +``` + +For binary content, use `blob` (base64-encoded) instead of `text`. + +Note: Naftiko schema uses `label`; MCP protocol descriptors expose the same value as `title` (tools, resources, and prompts). + +### `resources/templates/list` Response + +Resources whose `uri` contains `{param}` placeholders are advertised as templates: + +```json +{ + "resourceTemplates": [ + { + "uriTemplate": "data://users/{userId}/profile", + "name": "user-profile", + "title": "User Profile", + "description": "User profile by ID", + "mimeType": "application/json" + } + ] +} +``` + +### `prompts/list` Response + +```json +{ + "prompts": [ + { + "name": "summarize-data", + "title": "Summarize Data", + "description": "Summarize API response data for the user", + "arguments": [ + { + "name": "data", + "description": "The raw API response data to summarize", + "required": true + }, + { + "name": "format", + "description": "Desired output format", + "required": false + } + ] + } + ] +} +``` + +### `prompts/get` Response + +```json +{ + "messages": [ + { + "role": "user", + "content": { + "type": "text", + "text": "Summarize the following data in bullet-points format:\n\n{\"users\": 42, \"active\": 38}" + } + } + ] +} +``` + +### Updated `McpProtocolDispatcher.dispatch()` Switch + +```java +switch (rpcMethod) { + case "initialize": return handleInitialize(idNode); + case "notifications/initialized": return null; + case "tools/list": return handleToolsList(idNode); + case "tools/call": return handleToolsCall(idNode, params); + case "resources/list": return handleResourcesList(idNode); // NEW + case "resources/read": return handleResourcesRead(idNode, params); // NEW + case "resources/templates/list": return handleResourcesTemplatesList(idNode); // NEW + case "prompts/list": return handlePromptsList(idNode); // NEW + case "prompts/get": return handlePromptsGet(idNode, params); // NEW + case "ping": return buildJsonRpcResult(idNode, mapper.createObjectNode()); + default: return buildJsonRpcError(idNode, -32601, "Method not found: " + rpcMethod); +} +``` + +--- + +## Implementation Examples + +### Example 1: Weather Capability with Resources and Prompts + +```yaml +naftiko: "0.5" + +info: + label: "Weather Intelligence" + description: "Weather data with tools, readable resources, and prompt templates" + +capability: + consumes: + - type: "http" + namespace: "weather-api" + description: "OpenWeather API" + baseUri: "https://api.openweathermap.org/data/2.5/" + resources: + - name: "weather" + path: "weather" + operations: + - name: "get-current" + method: "GET" + inputParameters: + - name: "q" + in: "query" + outputParameters: + - name: "temp" + type: "number" + value: "{{$.main.temp}}" + + exposes: + - type: "mcp" + transport: "http" + address: "0.0.0.0" + port: 9091 + namespace: "weather-mcp" + description: "Weather MCP server with tools, resources, and prompts" + + # ── Tools (existing) ── + tools: + - name: "get-weather" + label: "Get Weather" + description: "Get current weather for a city" + inputParameters: + - name: "city" + type: "string" + description: "City name" + call: "weather-api.get-current" + with: + q: "{{city}}" + outputParameters: + - type: "number" + mapping: "{{temp}}" + + # ── Resources (NEW) ── + resources: + # Dynamic: backed by consumed operation + - name: "current-weather" + label: "Current Weather" + uri: "weather://cities/{city}/current" + description: "Current weather data for a city" + mimeType: "application/json" + call: "weather-api.get-current" + with: + q: "{{city}}" + + # Static: served from local files + - name: "weather-guide" + label: "Weather Guide" + uri: "docs://weather/guide" + description: "Guide to interpreting weather data and units" + mimeType: "text/markdown" + location: "file:///etc/naftiko/resources/weather-guide" + + # ── Prompts (NEW) ── + prompts: + # Inline template + - name: "forecast-summary" + label: "Forecast Summary" + description: "Generate a natural-language weather summary" + arguments: + - name: "city" + description: "City name for the forecast" + required: true + - name: "data" + description: "Raw weather data JSON" + required: true + template: + - role: "user" + content: "Summarize the weather for {{city}} based on this data:\n\n{{data}}\n\nProvide temperature, conditions, and a brief recommendation." + + # File-based template + - name: "weather-report" + label: "Weather Report" + description: "Detailed weather report prompt for multiple cities" + arguments: + - name: "cities" + description: "Comma-separated list of cities" + required: true + location: "file:///etc/naftiko/prompts/weather-report.md" +``` + +### Example 2: Documentation Server (Resources + Prompts, No Dynamic Data) + +```yaml +naftiko: "0.5" + +info: + label: "API Documentation Server" + description: "Serve API docs as MCP resources with analysis prompts" + +capability: + consumes: + - type: "http" + namespace: "placeholder" + description: "Placeholder consumed API (required by schema)" + baseUri: "https://httpbin.org" + resources: + - name: "health" + path: "/get" + operations: + - name: "ping" + method: "GET" + + exposes: + - type: "mcp" + transport: "stdio" + namespace: "docs-mcp" + description: "API documentation server with readable docs and analysis prompts" + + tools: + - name: "ping" + label: "Ping" + description: "Health check" + call: "placeholder.ping" + + resources: + - name: "api-reference" + label: "API Reference" + uri: "docs://api/reference" + description: "Complete API reference documentation" + mimeType: "text/markdown" + location: "file:///etc/naftiko/docs/api-reference" + + - name: "changelog" + label: "Changelog" + uri: "docs://api/changelog" + description: "API changelog and release notes" + mimeType: "text/markdown" + location: "file:///etc/naftiko/docs/changelog" + + prompts: + - name: "analyze-endpoint" + label: "Analyze Endpoint" + description: "Analyze an API endpoint for best practices" + arguments: + - name: "endpoint" + description: "The endpoint path (e.g., /users/{id})" + required: true + - name: "method" + description: "HTTP method (GET, POST, etc.)" + required: true + template: + - role: "user" + content: "Analyze the {{method}} {{endpoint}} endpoint for:\n- RESTful design compliance\n- Error handling completeness\n- Security considerations\n- Performance implications" +``` + +### Example 3: Notion Capability Extended (Adding Resources and Prompts to Existing) + +Shows how the existing Notion example can be non-disruptively extended: + +```yaml +naftiko: "0.5" + +info: + label: "Notion Integration" + description: "Notion with MCP tools, resources, and prompts" + +capability: + consumes: + - type: "http" + namespace: "notion" + description: "Notion API v1" + baseUri: "https://api.notion.com/v1/" + authentication: + type: "bearer" + token: "{{notion_api_key}}" + resources: + - path: "databases/{{datasource_id}}/query" + name: "query" + inputParameters: + - name: "datasource_id" + type: "string" + description: "Identifier" + operations: + - method: "POST" + name: "query-db" + body: | + { + "filter": { + "property": "Participation Status", + "select": { "equals": "Committed" } + } + } + + exposes: + - type: "mcp" + address: "localhost" + port: 9091 + namespace: "notion-mcp" + description: "Notion MCP server" + + # Existing tools — now include label metadata + tools: + - name: "query-database" + label: "Query Database" + description: "Query Notion pre-release participants" + call: "notion.query-db" + with: + datasource_id: "2fe4adce-3d02-8028-bec8-000bfb5cafa2" + outputParameters: + - type: "array" + mapping: "$.results" + items: + - type: "object" + properties: + name: + type: "string" + mapping: "$.properties.Name.title[0].text.content" + + # NEW: Resources + resources: + - name: "database-schema" + label: "Database Schema" + uri: "notion://databases/pre-release/schema" + description: "Schema of the pre-release participants database" + mimeType: "application/json" + call: "notion.query-db" + with: + datasource_id: "2fe4adce-3d02-8028-bec8-000bfb5cafa2" + + # NEW: Prompts + prompts: + - name: "participant-outreach" + label: "Participant Outreach" + description: "Draft outreach message to pre-release participants" + arguments: + - name: "participant_name" + description: "Name of the participant" + required: true + - name: "product_name" + description: "Name of the product" + required: true + template: + - role: "user" + content: "Draft a personalized outreach email to {{participant_name}} about the upcoming {{product_name}} pre-release. Be professional but friendly." +``` + +--- + +## Security Considerations + +### Static Resource Path Validation + +Static resources served from `location` directories must enforce strict path validation to prevent directory traversal: + +1. **`location` URI scheme**: Only `file:///` is accepted +2. **Resolved path containment**: The resolved absolute path of any requested file must be within the `location` directory +3. **Path segment validation**: Each path segment must match `^[a-zA-Z0-9._-]+$` (no `..`, no special characters) +4. **Symlink resolution**: Resolve symlinks before containment check + +These rules are identical to the security model described in the Agent Skills proposal for `ExposedSkill.location`. + +### Prompt Template Injection + +Prompt argument values are substituted into templates literally. The MCP server does not interpret argument values as templates — `{{nested}}` in an argument value is treated as literal text, not as a placeholder. + +### Resource URI Validation + +- Resource `uri` values are identifiers — they do not control file system access +- The `{param}` placeholder syntax in resource template URIs must match `^[a-zA-Z0-9_]+$` +- Resource URIs are validated at capability load time + +--- + +## Validation Rules + +### Tool Validation + +| Rule | Scope | Description | +|------|-------|-------------| +| **Unique name** | tools[] | Each tool `name` MUST be unique within the MCP server | +| **Unique label** | tools[] | Each tool `label` SHOULD be unique within the MCP server for clear UI display | +| **Call or steps** | McpTool | At least one execution source (`call` or `steps`) MUST be present | + +### Resource Validation + +| Rule | Scope | Description | +|------|-------|-------------| +| **Unique name** | resources[] | Each resource `name` MUST be unique within the MCP server | +| **Unique label** | resources[] | Each resource `label` SHOULD be unique within the MCP server for clear UI display | +| **Unique URI** | resources[] | Each resource `uri` MUST be unique within the MCP server | +| **Single source** | McpResource | Exactly one of `call`, `steps`, or `location` MUST be present | +| **Call reference** | McpResource.call | MUST reference a valid `{namespace}.{operationId}` in consumes | +| **Location scheme** | McpResource.location | MUST start with `file:///` | +| **Location exists** | McpResource.location | The resolved directory MUST exist at startup | + +### Prompt Validation + +| Rule | Scope | Description | +|------|-------|-------------| +| **Unique name** | prompts[] | Each prompt `name` MUST be unique within the MCP server | +| **Unique label** | prompts[] | Each prompt `label` SHOULD be unique within the MCP server for clear UI display | +| **Single source** | McpPrompt | Exactly one of `template` or `location` MUST be present | +| **Location scheme** | McpPrompt.location | MUST start with `file:///` | +| **Location exists** | McpPrompt.location | The resolved file MUST exist at startup | +| **Placeholder coverage** | McpPrompt | Every `{{arg}}` placeholder in the template SHOULD correspond to a declared argument | + +### Cross-Validation with Agent Skills Proposal + +When a `skill` adapter derives a tool `from` an `mcp` adapter, only tools are derivable — not resources or prompts. The Agent Skills proposal's `SkillTool.from.action` maps to tool names, not resource names or prompt names. + +--- + +## Implementation Roadmap + +### Phase 1: Schema & Spec +- Update `McpTool` definition to require `label` +- Add `McpResource`, `McpPrompt`, `McpPromptArgument`, `McpPromptMessage` definitions to `capability-schema.json` +- Update `ExposesMcp` with optional `resources` and `prompts` arrays +- Update specification document (README.md) with new object sections + +### Phase 2: Spec Classes +- Update `McpServerToolSpec.java` with `label` and serialization/deserialization coverage +- Create `McpServerResourceSpec.java` (parallel to `McpServerToolSpec`) +- Create `McpServerPromptSpec.java` +- Update `McpServerSpec.java` with `List resources` and `List prompts` + +### Phase 3: Protocol Handlers +- Add `resources/list`, `resources/read`, `resources/templates/list` to `McpProtocolDispatcher` +- Add `prompts/list`, `prompts/get` to `McpProtocolDispatcher` +- Update `handleInitialize()` to advertise `resources`/`prompts` capabilities conditionally + +### Phase 4: Resource Execution +- Create `McpResourceHandler.java` (parallel to `McpToolHandler`) + - Dynamic: reuse `OperationStepExecutor` (same as tools) + - Static: file reader with path validation and MIME type detection + +### Phase 5: Prompt Rendering +- Create `McpPromptHandler.java` + - Inline: argument substitution in message templates + - File-based: file reader + argument substitution + +### Phase 6: Testing +- Unit tests for each new spec class (round-trip serialization) +- Integration tests for each resource type (dynamic, static) +- Integration tests for each prompt type (inline, file-based) +- Security tests for path traversal and prompt injection + +--- + +## Backward Compatibility + +This proposal is **fully backward compatible**: + +1. **`resources` is optional** — existing MCP adapters without resources continue to work unchanged +2. **`prompts` is optional** — existing MCP adapters without prompts continue to work unchanged +3. **Tool execution unchanged** — `McpTool` adds metadata (`label`) only; no changes to execution semantics +4. **Protocol backward compatible** — existing `tools/list` and `tools/call` methods unchanged; new methods return `-32601` (method not found) only if the client calls them on a server that doesn't support them +5. **`initialize` additive** — capabilities object adds `resources`/`prompts` alongside existing `tools`; clients that don't understand them ignore them + +### Consistency with Agent Skills Proposal + +| Pattern | Agent Skills Proposal | This Proposal | +|---------|----------------------|---------------| +| `location` URI | `file:///` → directory with supporting files | `file:///` → directory (resources) or file (prompts) | +| File serving | `/contents/{file}` REST endpoint | `resources/read` MCP method | +| Path validation | Regex + containment check | Same regex + containment check | +| Metadata-first | Skills describe, don't execute | Resources describe content source | +| No new execution model | Derived tools use existing adapters | Dynamic resources use existing `call`/`steps` | diff --git a/src/main/resources/wiki/Contribute.md b/src/main/resources/wiki/Contribute.md new file mode 100644 index 0000000..6d82779 --- /dev/null +++ b/src/main/resources/wiki/Contribute.md @@ -0,0 +1,11 @@ +With welcome ALL contributions to Naftiko Framework, from the smallest to the largest, they all make a positive impact. + + - **Bugs** and **Enhancements** should be entered in the [Issue Tracker](/naftiko/framework/issues) and discussed there directly + - :beginner: _Please search existing issues to limit the creation of duplicates_ + + - **Code contributions** should be prepared in a local branch and submitted via a [Pull Request](/naftiko/framework/pulls) + - :beginner: _Please make sure that your code pass all the build validation and rebase on "main" branch before asking for a review_ + + - **All contributions** are accepted under the [Apache 2.0 License](/naftiko/framework/blob/main/LICENSE) + - ⚠️ : _You need to ensure you have full rights on the code you are submitting, for example from your employer_ + \ No newline at end of file diff --git a/src/main/resources/wiki/FAQ,md b/src/main/resources/wiki/FAQ,md new file mode 100644 index 0000000..0d5d82a --- /dev/null +++ b/src/main/resources/wiki/FAQ,md @@ -0,0 +1,822 @@ +Welcome to the Naftiko Framework FAQ! This guide answers common questions from developers who are learning, using, and contributing to Naftiko. For comprehensive technical details, see the [Specification](https://github.com/naftiko/framework/wiki/Specification). + +--- + +## ⛵ Getting Started + +### Q: What is Naftiko Framework and why would I use it? +**A:** Naftiko Framework is the first open-source platform for **Spec-Driven AI Integration**. Instead of writing boilerplate code to consume APIs and expose unified interfaces, you declare them in YAML. This enables: +- **API composability**: Combine multiple APIs into a single capability +- **Format conversion**: Convert between JSON, XML, Avro, Protobuf, CSV, and YAML +- **AI-ready integration**: Better context engineering for AI systems +- **Reduced API sprawl**: Manage microservices and SaaS complexity + +Use it when you need to integrate multiple APIs, standardize data formats, or expose simplified interfaces to AI agents. + +### Q: What skills do I need to create a capability? +**A:** You only need to know: +- **YAML syntax** - the configuration language for capabilities +- **JSONPath** - for extracting values from JSON responses +- **Mustache templates** - for injecting parameters (optional, if using advanced features) + +You don't need to write Java or other code unless you want to extend the framework itself. + +### Q: Is Naftiko a code generator or a runtime engine? +**A:** It's a **runtime engine**. The Naftiko Engine, provided as a Docker container, reads your YAML capability file at startup and immediately exposes HTTP or MCP interfaces. There's no compilation step - declare your capability, start the engine, and it works. + +--- + +## 🚢 Installation & Setup + +### Q: How do I install Naftiko? +**A:** There are two ways: + +1. **Docker (recommended)** + ```bash + docker pull ghcr.io/naftiko/framework:v0.4 + docker run -p 8081:8081 -v /path/to/capability.yaml:/app/capability.yaml ghcr.io/naftiko/framework:v0.4 /app/capability.yaml + ``` + +2. **CLI tool** (for configuration and validation) + Download the binary for [macOS](https://github.com/naftiko/framework/releases/download/v0.4/naftiko-cli-macos-arm64), [Linux](https://github.com/naftiko/framework/releases/download/v0.4/naftiko-cli-linux-amd64), or [Windows](https://github.com/naftiko/framework/releases/download/v0.4/naftiko-cli-windows-amd64.exe) + +See the [Installation guide](https://github.com/naftiko/framework/wiki/Installation) for detailed setup instructions. + +### Q: How do I validate my capability file before running it? +**A:** Use the CLI validation command: +```bash +naftiko validate path/to/capability.yaml +naftiko validate path/to/capability.yaml 0.4 # Specify schema version +``` + +This checks your YAML against the Naftiko schema and reports any errors. + +### Q: Which version of the schema should I use? +**A:** Use the latest stable version: **0.4** (as of March 2026). + +Set it in your YAML: +```yaml +naftiko: "0.4" +``` + +--- + +## 🧭 Core Concepts + +### Q: What are "exposes" and "consumes"? +**A:** These are the two essential parts of every capability: + +- **Exposes** - What your capability *provides* to callers (HTTP API or MCP server) +- **Consumes** - What external APIs your capability *uses internally* + +Example: A capability that consumes the Notion API and GitHub API, then exposes them as a single unified REST endpoint or MCP tool. + +### Q: What's the difference between API and MCP exposure? +**A:** + +| Feature | API (REST) | MCP | +|---------|-----------|-----| +| **Protocol** | HTTP/REST | Model Context Protocol (JSON-RPC) | +| **Best for** | General-purpose integrations, web apps | AI agent-native integrations | +| **Tool discovery** | Manual or via OpenAPI | Automatic via MCP protocol | +| **Configuration** | `type: "api"` with resources/operations | `type: "mcp"` with tools | +| **Default transport** | HTTP | stdio or HTTP (streamable) | + +**Use API** for traditional REST clients, web applications, or when you want standard HTTP semantics. +**Use MCP** when exposing capabilities to AI agents or Claude. + +### Q: What is a "namespace"? +**A:** A namespace is a **unique identifier** for a consumed or exposed source, used for routing and references. + +- **In consumes**: `namespace: github` means "this is the GitHub API I'm consuming" +- **In exposes**: `namespace: app` means "my exposed API is called `app`" +- **In steps**: `call: github.get-user` routes to the consumed `github` namespace + +Namespaces must be unique within their scope (all consumed namespaces must differ, all exposed namespaces must differ). + +### Q: What are "steps" and when do I use them? +**A:** Steps enable **multi-step orchestration** - calling multiple APIs in sequence and combining their results. + +**Simple mode** (direct call): +```yaml +operations: + - method: GET + call: github.get-user # Call one consumed operation directly + with: + username: "{{github_username}}" +``` + +**Orchestrated mode** (multi-step): +```yaml +operations: + - name: complex-operation + method: GET + steps: + - type: call + name: step1 + call: github.list-users + - type: call + name: step2 + call: github.get-user + with: + username: $step1.result # Use output from step1 + mappings: + - targetName: output_field + value: $.step2.userId +``` + +Use steps when your capability needs to combine data from multiple sources or perform lookups. + +### Q: What's the difference between "call" and "lookup" steps? +**A:** + +- **`call` steps**: Execute a consumed operation (HTTP request) +- **`lookup` steps**: Search through a previous step's output for matching records + +Example: Call an API to list all users, then lookup which one matches a given email: +```yaml +steps: + - type: call + name: list-all-users + call: hr.list-employees + - type: lookup + name: find-user-by-email + index: list-all-users + match: email + lookupValue: "{{email_to_find}}" + outputParameters: + - fullName + - department +``` + +--- + +## 🔩 Configuration & Parameters + +### Q: How do I inject input parameters into a consumed operation? +**A:** Use the `with` injector in your exposed operation: + +**Simple mode:** +```yaml +operations: + - method: GET + call: github.get-user + with: + username: "{{github_username}}" # From externalRefs + accept: "application/json" # Static value +``` + +**Orchestrated mode (steps):** +```yaml +steps: + - type: call + name: fetch-user + call: github.get-user + with: + username: "{{github_username}}" +``` + +The `with` object maps consumed operation parameter names to: +- Variable references like `{{variable_name}}` injected from externalRefs +- Static strings or numbers - literal values + +### Q: How do I extract values from API responses (output parameters)? +**A:** Use **JsonPath expressions** in the `value` field of `outputParameters`: + +```yaml +consumes: + - resources: + - operations: + - outputParameters: + - name: userId + type: string + value: $.id # Top-level field + - name: email + type: string + value: $.contact.email # Nested field + - name: allNames + type: array + value: $.users[*].name # Array extraction +``` + +Common JsonPath patterns: +- `$.fieldName` - access a field +- `$.users[0].name` - access array element +- `$.users[*].name` - extract all `.name` values from array +- `$.data.user.email` - nested path + +### Q: What are "mappings" and when do I use them? +**A:** Mappings connect step outputs to exposed operation outputs in multi-step orchestrations. + +```yaml +steps: + - type: call + name: fetch-db + call: notion.get-database + - type: call + name: query-db + call: notion.query-database + +mappings: + - targetName: database_name # Exposed output parameter + value: $.fetch-db.dbName # From first step's output + - targetName: row_count + value: $.query-db.resultCount # From second step + +outputParameters: + - name: database_name + type: string + - name: row_count + type: number +``` + +Mappings tell Naftiko how to wire step outputs to your final response. + +--- + +## 🗝️ Authentication & Security + +### Q: How do I authenticate to external APIs? +**A:** Add an `authentication` block to your `consumes` section: + +```yaml +consumes: + - type: http + namespace: github + baseUri: https://api.github.com + authentication: + type: bearer + token: "{{github_token}}" # Use token from externalRefs +``` + +**Supported authentication types:** +- `bearer` - Bearer token +- `basic` - Username/password +- `apikey` - Header or query parameter API key +- `digest` - HTTP Digest authentication + +### Q: How do I manage secrets like API tokens? +**A:** Use **externalRefs** to declare variables that are injected at runtime: + +```yaml +externalRefs: + - name: secrets + type: environment + resolution: runtime + keys: + github_token: GITHUB_TOKEN # Maps env var to template variable + notion_token: NOTION_TOKEN + +consumes: + - namespace: github + authentication: + type: bearer + token: "{{github_token}}" # Use the injected variable + - namespace: notion + authentication: + type: bearer + token: "{{notion_token}}" +``` + +**At runtime, provide environment variables:** +```bash +docker run -e GITHUB_TOKEN=ghp_xxx -e NOTION_TOKEN=secret_xxx ... +``` + +> ⚠️ **Security note**: Use `resolution: runtime` in production (not `file`). Never commit secrets to your repository. + +### Q: Can I authenticate to exposed endpoints (API/MCP)? +**A:** Yes, add `authentication` to your `exposes` block: + +```yaml +exposes: + - type: api + port: 8081 + namespace: my-api + authentication: + type: apikey + in: header + name: X-Api-Key + value: "{{api_key}}" + resources: + - path: /data + description: Protected data endpoint +``` + +**Supported authentication types for exposed endpoints:** +- `apikey` - API key via header or query parameter +- `bearer` - Bearer token validation +- `basic` - Username/password via HTTP Basic Auth + +### Q: Can I send complex request bodies (JSON, XML, etc.)? +**A:** Yes, use the `body` field for request bodies: + +```yaml +consumes: + - resources: + - operations: + - method: POST + body: + type: json + data: + filter: + status: "active" +``` + +**Body types:** +- `json` - JSON object or string +- `text`, `xml`, `sparql` - Plain text payloads +- `formUrlEncoded` - URL-encoded form +- `multipartForm` - Multipart file upload + +--- + +## 🗺️ API Design + +### Q: How do I define resource paths with parameters? +**A:** Use path parameters with curly braces: + +```yaml +exposes: + - resources: + - path: /users/{userId}/projects/{projectId} + description: Get a specific project for a user + inputParameters: + - name: userId + in: path + type: string + description: The user ID + - name: projectId + in: path + type: string + description: The project ID +``` + +Callers access it as: `GET /users/123/projects/456` + +### Q: How do I support query parameters and headers? +**A:** Use `in` field in `inputParameters`: + +```yaml +inputParameters: + - name: filter + in: query + type: string + description: Filter results + - name: Authorization + in: header + type: string + description: Auth header + - name: X-Custom + in: header + type: string + description: Custom header +``` + +Callers send: `GET /endpoint?filter=value` with custom headers. + +### Q: How do forward proxies work? +**A:** Use `forward` to pass requests through to a consumed API without transformation: + +```yaml +exposes: + - resources: + - path: /github/{path} + description: Pass-through proxy to GitHub API + forward: + targetNamespace: github + trustedHeaders: + - Authorization + - Accept +``` + +This forwards `GET /github/repos/owner/name` to GitHub's `/repos/owner/name`. + +**Trusted headers** must be explicitly listed for security. + +--- + +## 📡 MCP-Specific + +### Q: How do I expose a capability as an MCP tool? +**A:** Use `type: mcp` in exposes instead of `type: api`: + +```yaml +exposes: + - type: mcp + address: localhost + port: 9091 + namespace: my-mcp + description: My MCP server + tools: + - name: query-database + description: Query the database + call: notion.query-db + with: + db_id: "fixed-db-id" + outputParameters: + - type: array + mapping: $.results +``` + +### Q: What's the difference between HTTP and stdio MCP transports? +**A:** + +| Transport | Use Case | Setup | +|-----------|----------|-------| +| **HTTP** | Streamable HTTP transport, integrates with existing infrastructure | Specify `address` and `port` | +| **stdio** | Direct process communication, native integration with Claude Desktop | No address/port needed | + +For Claude integration, stdio is typically preferred. HTTP is useful for remote or containerized deployments. + +### Q: How do I expose MCP resources and prompts? +**A:** Add `resources` and `prompts` sections to your MCP server: + +```yaml +exposes: + - type: mcp + resources: + - uri: file:///docs/guide.md + name: User Guide + description: API usage guide + prompts: + - name: analyze-code + description: Analyze code snippet + template: "Analyze this code:\n{{code}}" +``` + +MCP clients can then discover and use these resources dynamically. + +--- + +## 🔭 Troubleshooting & Debugging + +### Q: My capability won't start. How do I debug it? +**A:** + +1. **Validate your YAML first:** + ```bash + naftiko validate capability.yaml + ``` + +2. **Check the Docker logs:** + ```bash + docker run ... ghcr.io/naftiko/framework:v0.4 /app/capability.yaml + # Look for error messages in the output + ``` + +3. **Verify your file path** - if using Docker, ensure: + - The volume mount is correct: `-v /absolute/path:/app/capability.yaml` + - The file exists and is readable + - For Docker on Windows/Mac, use proper path translation + +4. **Check external services** - ensure: + - APIs you're consuming are reachable + - Network connectivity is available + - Authentication credentials are correct + +### Q: Requests to my exposed endpoint return errors. How do I debug? +**A:** + +1. **Check the request format** - ensure headers, parameters, and body match your definition +2. **Verify consumed API availability** - test the underlying API directly +3. **Inspect JsonPath mappings** - ensure your extraction paths match the API response +4. **Use Docker logs** - see server-side error messages + +### Q: JsonPath expressions aren't extracting the data I expect. How do I fix it? +**A:** + +1. **Test your JsonPath** - use an online tool like [jsonpath.com](https://jsonpath.com) +2. **Inspect the actual response** - add an operation without filtering to see raw data +3. **Understand array syntax**: + - `$.users[0]` - first element + - `$.users[*]` - all elements (creates array output) + - `$.users[*].name` - all names + +4. **For nested objects**, trace the path step-by-step: `$.data.user.profile.email` + +### Q: My parameters aren't being passed to the consumed API. What's wrong? +**A:** + +1. **Check parameter names match** - consumed parameter names must match keys in `with` +2. **Verify parameter location** (`in: path`, `in: query`, `in: header`, etc.) +3. **Check variable references** - ensure `{{variable_name}}` variables are defined in externalRefs +4. **Test without transformation** - use `forward` to proxy the request and see if underlying API works + +### Q: Authentication is failing. How do I debug it? +**A:** + +1. **Test credentials directly** - verify your token/key works with the API +2. **Check token format** - ensure it's a valid token (not expired, wrong format, etc.) +3. **Verify placement** - is the token in the right header/query/body? +4. **Environment variables** - ensure the Docker environment variable matches the key name in `externalRefs` +5. **Quotes** - make sure tokens with special characters are properly quoted in YAML + +--- + +## 🚣 Contributing + +### Q: How do I contribute to Naftiko Framework? +**A:** We welcome all contributions! Here's how: + +1. **Report bugs or request features** - [GitHub Issues](https://github.com/naftiko/framework/issues) + - Search for existing issues first to avoid duplicates + +2. **Submit code changes** - [GitHub Pull Requests](https://github.com/naftiko/framework/pulls) + - Create a local branch + - Ensure your code passes all build validation + - Rebase on `main` before submitting + +3. **Contribute examples** - Add capability examples to the repository + - Document your use case in the example + - Include comments explaining key features + +4. **Improve documentation** - Fix typos, clarify docs, add examples + +### Q: What's the code structure and how do I set up a development environment? +**A:** Naftiko is a **Java project** using Maven. To build and develop: + +```bash +# Clone the repository +git clone https://github.com/naftiko/framework.git +cd framework + +# Build the project +mvn clean install + +# Run tests +mvn test + +# Build Docker image +docker build -t naftiko:local . +``` + +Key directories: +- `src/main/java/io/naftiko/` Core engine code +- `src/main/resources/schemas/` JSON Schema definitions +- `src/test/` Unit and integration tests +- `src/main/resources/specs/` Specification proposals and examples + +### Q: What are the design guidelines for creating capabilities? +**A:** + +1. **Keep the Naftiko Specification as a first-class citizen** - refer to it often +2. **Don't expose unused input parameters** - every parameter should be used in steps +3. **Don't declare consumed outputs you don't use** - be precise in mappings +4. **Don't prefix variables unnecessarily** - let scope provide clarity + +Example: +```yaml +# Good: expose only used input +inputParameters: + - name: database_id # Used in step below + in: path + +# Bad: expose unused input +inputParameters: + - name: database_id + - name: unused_param # Never used anywhere + +# Good: output only consumed outputs you map +outputParameters: + - name: result + value: $.step1.output # Clearly mapped + +# Bad: declare outputs you don't use +outputParameters: + - name: unused_result + value: $.step1.unused +``` + +### Q: How do I test my capability changes? +**A:** + +1. **Unit tests** - Add tests in `src/test/java` +2. **Integration tests** - Test against real or mock APIs +3. **Validation** - Use the CLI tool: `naftiko validate capability.yaml` +4. **Docker testing** - Build and run the Docker image with your capability + +### Q: Which version of Java is required? +**A:** Naftiko requires **Java 17 or later**. This is specified in the Maven configuration. + +--- + +## ⛴️ Advanced Topics + +### Q: Can I use templates/variables in my capability definition? +**A:** Yes, use **Mustache-style `{{variable}}`** expressions: + +```yaml +externalRefs: + - name: env + type: environment + keys: + api_key: API_KEY + base_url: API_BASE_URL + +consumes: + - baseUri: "{{base_url}}" + authentication: + type: apikey + key: X-API-Key + value: "{{api_key}}" +``` + +Variables come from `externalRefs` and are injected at runtime. + + +### Q: Can I compose capabilities (capability calling another capability)? +**A:** Indirectly - by referencing the exposed URL/port as a consumed API: + +```yaml +# Capability B "consumes" the exposed endpoint from Capability A +consumes: + - baseUri: http://localhost:8081 # Capability A's port + namespace: capability-a +``` + +This way, Capability B can combine Capability A with other APIs. + +### Q: How do I handle errors or retries? +**A:** Naftiko currently doesn't have built-in retry logic in v0.4. Options: + +1. **At the HTTP client level** - use an API gateway with retry policies +2. **In future versions** - this is on the roadmap + +Check the [Roadmap](https://github.com/naftiko/framework/wiki/Roadmap) for planned features. + +### Q: Can I expose the same capability on both API and MCP? +**A:** Yes! Add multiple entries to `exposes`: + +```yaml +exposes: + - type: api + port: 8081 + namespace: rest-api + resources: [...] + + - type: mcp + port: 9091 + namespace: mcp-server + tools: [...] + +consumes: [...] # Shared between both +``` + +Both adapters consume the same sources but expose different interfaces. + +--- + +## 💨 Performance & Deployment + +### Q: How scalable is Naftiko for high-load scenarios? +**A:** Naftiko is suitable for moderate to high loads depending on: +- **Your consumed APIs' performance** - Naftiko's overhead is minimal +- **Docker/Kubernetes scaling** - deploy multiple instances behind a load balancer +- **Orchestration complexity** - simpler capabilities (forward, single calls) are faster + +For production workloads: +- Use Kubernetes for auto-scaling +- Monitor consuming/consumed API latencies +- Consider caching strategies above Naftiko + +### Q: How do I deploy Naftiko to production? +**A:** + +1. **Kubernetes** (recommended): + ```yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: naftiko-engine + spec: + replicas: 3 + template: + spec: + containers: + - name: naftiko + image: ghcr.io/naftiko/framework:v0.4 + volumeMounts: + - name: capability + mountPath: /app/capability.yaml + subPath: capability.yaml + env: + - name: GITHUB_TOKEN + valueFrom: + secretKeyRef: + name: naftiko-secrets + key: github-token + ``` + +2. **Docker Compose** - for simpler setups +3. **Environment Variables** - inject secrets via `externalRefs` with `resolution: runtime` + +### Q: Can I use Naftiko behind a reverse proxy (nginx, Envoy)? +**A:** Yes, absolutely. Naftiko exposes standard HTTP endpoints, so it works with any reverse proxy. + +Example (nginx): +```nginx +server { + listen 80; + location / { + proxy_pass http://naftiko:8081; + } +} +``` + +--- + +## 📜 Specifications & Standards + +### Q: How does Naftiko compare to OpenAPI, AsyncAPI, or Arazzo? +**A:** Naftiko is **complementary** to these specifications and combines their strengths into a single runtime model: +- **Consume/expose duality** - like OpenAPI's interface description, but bidirectional +- **Orchestration** - like Arazzo's workflow sequencing +- **AI-driven discovery** - beyond what all three cover natively +- **Namespace-based routing** - unique to Naftiko's runtime approach + +See the [Specification](https://github.com/naftiko/framework/wiki/Specification#13-related-specifications) for a detailed comparison. + +### Q: Is the Naftiko Specification stable? +**A:** Yes, v0.4 is stable as of March 2026. The specification follows semantic versioning: +- **Major versions** (0.x.0) - breaking changes +- **Minor versions** (x.1.0) - new features, backward-compatible +- **Patch versions** (x.x.1) - bug fixes + +Check the naftiko field in your YAML to specify the version. + +--- + +## 📣 Community & Support + +### Q: Where can I ask questions or discuss ideas? +**A:** Join the community at: +- **[GitHub Discussions](https://github.com/orgs/naftiko/discussions)** - Ask questions and share ideas +- **[GitHub Issues](https://github.com/naftiko/framework/issues)** - Report bugs or request features +- **Pull Requests** - Review and discuss code changes + +### Q: Are there examples I can reference? +**A:** Yes! Several resources: + +- **[Tutorial](https://github.com/naftiko/framework/wiki/Tutorial)** - Step-by-step guides +- **[Use Cases](https://github.com/naftiko/framework/wiki/Use-cases)** - Real-world examples +- **Repository examples** - In `src/main/resources/specs/` and test resources +- **Specification examples** - In the [Specification](https://github.com/naftiko/framework/wiki/Specification#4-complete-examples) (Section 4) + +### Q: How often is Naftiko updated? +**A:** Check the [Releases](https://github.com/naftiko/framework/wiki/Releases) page for version history. The project follows a regular release cadence with security updates prioritized. + +--- + +## 🚤 Common Use Cases + +### Q: I want to create a unified API that combines Notion + GitHub. How do I start? +**A:** + +1. **Read the Tutorial** - particularly steps 2-5 on forwarding and orchestration +2. **Define consumed sources** - GitHub and Notion APIs with auth +3. **Design exposed resources** - endpoints that combine their data +4. **Use multi-step orchestration** - call both APIs and map results +5. **Test locally** - use Docker to run your capability + +### Q: I want to expose my capability as an MCP tool for Claude. How do I do this? +**A:** + +1. **Use `type: mcp`** in `exposes` +2. **Define `tools`** - each tool is an MCP tool your capability provides +3. **Use stdio transport** - for native Claude Desktop integration +4. **Test with Claude** - configure Claude Desktop with your MCP server +5. **Publish** - share your capability spec with the community + +See the [Tutorial](https://github.com/naftiko/framework/wiki/Tutorial) Section 6 (MCP) for a full example. + +### Q: I want to standardize data from multiple SaaS tools. How do I use Naftiko? +**A:** + +1. **Consume multiple SaaS APIs** - define each in `consumes` +2. **Normalize outputs** - use `outputParameters` to extract and structure data consistently +3. **Expose unified interface** - create a single API with harmonized formats +4. **Use orchestration** - combine data from multiple sources if needed + +This is Naftiko's core strength for managing API sprawl. + +--- + +## 🏝️ Additional Resources + +- **[Specification](https://github.com/naftiko/framework/wiki/Specification)** - Complete technical reference +- **[Tutorial](https://github.com/naftiko/framework/wiki/Tutorial)** - Step-by-step learning guide +- **[Installation](https://github.com/naftiko/framework/wiki/Installation)** - Setup instructions +- **[Use Cases](https://github.com/naftiko/framework/wiki/Use-cases)** - Real-world examples +- **[Roadmap](https://github.com/naftiko/framework/wiki/Roadmap)** - Future plans +- **[Contribute](https://github.com/naftiko/framework/wiki/Contribute)** - How to contribute +- **[Discussions](https://github.com/orgs/naftiko/discussions)** - Community Q&A + +--- + +## 🔔 Feedback + +Did this FAQ help you? Have questions not covered here? +- **Add an issue** - [GitHub Issues](https://github.com/naftiko/framework/issues) +- **Start a discussion** - [GitHub Discussions](https://github.com/orgs/naftiko/discussions) +- **Submit a PR** - Help us improve this FAQ! \ No newline at end of file diff --git a/src/main/resources/wiki/Installation.md b/src/main/resources/wiki/Installation.md new file mode 100644 index 0000000..1910bf0 --- /dev/null +++ b/src/main/resources/wiki/Installation.md @@ -0,0 +1,131 @@ +To use Naftiko Framework, you must install and then run its engine. + +## Docker usage +### Prerequisites +* You need Docker or, if you are on macOS or Windows their Docker Desktop version. To do so, follow the official documentation: + * [For Mac](https://docs.docker.com/desktop/setup/install/mac-install/) + * [For Linux](https://docs.docker.com/desktop/setup/install/linux/) + * [For Windows](https://docs.docker.com/desktop/setup/install/windows-install/) + +* Be sure that Docker (or Docker Desktop) is running + +### Pull Naftiko's Docker image +* Naftiko provides a docker image hosted in GitHub packages platform. It is public, so you can easily pull it locally. + ```bash + # v0.4 + docker pull ghcr.io/naftiko/framework:sha-86377ea + + # If you want to play with the last snapshot + docker pull ghcr.io/naftiko/framework:latest + ``` + Then, you should see the image 'ghcr.io/naftiko/framework' in your Docker Desktop with the tag 'latest'. You can also display local images with this command: + ```bash + docker image ls + ``` + +### Configure your own capability +* Create your capability configuration file.\ + The Naftiko Engine runs capabilities. For that, it uses a capability configuration file. You first have to create this file locally. You can use [this "Hello, World!" example](https://github.com/naftiko/framework/blob/main/src/main/resources/schemas/tutorial/step1-hello-world.yml) to start with and then move to the [Tutorial](https://github.com/naftiko/framework/wiki/Tutorial) and later to the comprehensive [Naftiko Specification](https://github.com/naftiko/framework/wiki/Specification). This file must be a YAML file (yaml and yml extensions are supported). + +* Local hosts in your capability configuration file.\ + If your capability refers to some local hosts, be careful to not use 'localhost', but 'host.docker.internal' instead. This is because your capability will run into an isolated docker container, so 'localhost' will refer to the container and not your local machine. + For example: + ```bash + baseUri: "http://host.docker.internal:8080/api/" + ``` + +### Run Naftiko Engine as a Docker container +* Use a Docker volume.\ + As you have to provide your local capability configuration file to the docker container, you must use a volume. This will be done using the '-v' option of the docker run command. + +* Use port forwarding.\ + According to your configuration file, your capability will be exposed on a given port. Keep in mind that the framework engine runs in a container context, so this port won't be accessible from your local machine. You must use the port forwarding. This will be done using the '-p' option of the docker run command. + +* Run your capability with Naftiko Engine.\ + Given a capability configuration file 'test.capability.yaml' and an exposition on port 8081, here is the command you have to execute to run the Framework Engine: + ```bash + docker run -p 8081:8081 -v full_path_to_your_capability_folder/test.capability.yaml:/app/test.capability.yaml ghcr.io/naftiko/framework:latest /app/test.capability.yaml + ``` + Then you should be able to request your capability at http://localhost:8081 + +## CLI tool +The Naftiko framework provides a CLI tool.\ +The goal of this CLI is to simplify configuration and validation. While everything can be done manually, the CLI provides helper commands. + +## Installation +### macOS +For the moment, CLI is only provided for Apple Silicon (with M chip). +**Apple Silicon (M1/M2/M3/M4):** +```bash +# Download the binary +curl -L https://github.com/naftiko/framework/releases/download/v0.4/naftiko-cli-macos-arm64 -o naftiko + +# Set binary as executable +chmod +x naftiko + +# Delete the macOS quarantine (temporary step, because the binary is not signed yet) +xattr -d com.apple.quarantine naftiko + +# Install +sudo mv naftiko /usr/local/bin/ +``` +### Linux +```bash +# Download the binary +curl -L https://github.com/naftiko/framework/releases/download/v0.4/naftiko-cli-linux-amd64 -o naftiko + +# Set binary as executable +chmod +x naftiko + +# Install +sudo mv naftiko /usr/local/bin/ +``` +### Windows +PowerShell installation is recommended. + +**Open PowerShell as admin and execute:** +```powershell +# Create installation folder +New-Item -ItemType Directory -Force -Path "C:\Program Files\Naftiko" + +# Download the binary +Invoke-WebRequest -Uri "https://github.com/naftiko/framework/releases/download/v0.4/naftiko-cli-windows-amd64.exe" -OutFile "C:\Program Files\Naftiko\naftiko.exe" + +# Add to the system PATH +$oldPath = [Environment]::GetEnvironmentVariable('Path', 'Machine') +$newPath = $oldPath + ';C:\Program Files\Naftiko' +[Environment]::SetEnvironmentVariable('Path', $newPath, 'Machine') +``` + +## Test +After installation, you may have to restart your terminal. Then run this command to check the CLI is well installed: +```bash +naftiko --help +``` +You should see the help of the command. + +## Use +There are two available features for the moment: the creation of a "minimum" valid capability configuration file, and the validation of a capability file. +### Create a capability configuration file +```bash +naftiko create capability +# You can also use aliases like: +naftiko cr cap +naftiko c cap +``` +The terminal will then ask you several questions. Finally, the file will be generated in your current directory. +### Validate a capability configuration file +The capabilities configuration file generated by the previous command should be valid. However, you can then complete it or even create it from scratch.\ +The validation command allows you to check your file. +```bash +naftiko validate path_to_your_capability_file +# You can also use aliases like: +naftiko val path_to_your_capability_file +naftiko v path_to_your_capability_file +``` +By default, validation is performed on the latest schema version. If you want to test validation on a previous schema version, you can specify it as the second argument. +```bash +# Validate the capability configuration file with the schema v0.3 +naftiko validate path_to_your_capability_file 0.3 +``` +The result will tell you if the file is valid or if there are any errors. diff --git a/src/main/resources/wiki/Releases.md b/src/main/resources/wiki/Releases.md new file mode 100644 index 0000000..5c93d36 --- /dev/null +++ b/src/main/resources/wiki/Releases.md @@ -0,0 +1,7 @@ +| Version | Requirements | Release | EOL | Java EOL | Maven Group ID | Maven Repo | Docker Repo | +| ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | ------------- | +| [0.4](https://github.com/naftiko/framework/releases/tag/v0.4) | Java 21 LTS | 2026-03-03 | TBD | Sept. 2028 | io.naftiko | GitHub Packages | GitHub Packages | +| 0.5 * | Java 21 LTS | 2026-03-16 * | TBC | Sept. 2028 | io.naftiko | GitHub Packages | GitHub Packages | +| 1.0 * | Java 25 LTS | 2026-04-? * | TBC | Sept. 2030 | io.naftiko | Maven Cental | Docker Hub | + +\* Future release \ No newline at end of file diff --git a/src/main/resources/wiki/Roadmap.md b/src/main/resources/wiki/Roadmap.md new file mode 100644 index 0000000..d42bf10 --- /dev/null +++ b/src/main/resources/wiki/Roadmap.md @@ -0,0 +1,52 @@ +## Version 1.0 - First Alpha - March 30th, 2026 :seedling: + +The goal of this version is to deliver a MVP to enable common AI integration use cases and grow our community. + +### Rightsize AI context +- [ ] Declarative applied capability exposing Agent Skills +- [ ] Declarative MCP exposing Resources and Prompts (Tools only so far) + +### Enable API reuse +- [ ] Reusable source HTTP adapter declaration across capabilities + - [ ] Declarative applied capabilities with reused source capabilities +- [ ] Support for lookups as part of API call steps +- [ ] Authenticate API and MCP Server consumers and manage permissions + +### Core developer experience +- [ ] Provide per-capability Control API and MCP adapters, aligned with CLI +- [ ] Provide GitHub Action template based on [Super Linter](https://github.com/super-linter/super-linter) +- [ ] Publish Maven Artifacts to [Maven Central](https://central.sonatype.com/) +- [ ] Publish Javadocs to [Javadoc.io](https://javadoc.io) +- [ ] Publish Docker Image to [Docker Hub](https://hub.docker.com/) +- [ ] Publish JSON Schema to [JSON Schema Store](https://www.schemastore.org/) +- [ ] Publish Naftiko JSON Structure +- [ ] Publish FAQ in the wiki + +## Version 1.0 - Second Alpha - April 27th :deciduous_tree: + +The goal of this version is to deliver a MVP to enable common AI integration use cases and grow our community. + +- [ ] Enable agent orchestration use cases + - [ ] Declarative applied capability exposing A2A +- [ ] Provide enhanced security + - [ ] Facilitate integration with various API/MCP/AI gateways + - [ ] Facilitate integration with [Keycloak](https://www.keycloak.org/), [OpenFGA](https://openfga.dev/) +- [ ] Provide Control webapp (per Capability) +- [ ] Publish Docker Desktop Extension to Docker Hub +- [ ] Fabric discovery of published capabilities for consumers + +## Version 1.0 - First Beta - June :blossom: + +The goal of this version is to deliver a stable MVP, including a stable Naftiko Specification + +- [ ] Incorporate community feedback +- [ ] Solidify the existing alpha version scope +- [ ] Increase test coverage and overall quality + +## Version 1.0 - General Availability - September :apple: + +The goal of this version is to release our first version ready for production. + +- [ ] Incorporate community feedback +- [ ] Solidify the existing beta version scope +- [ ] Increase test coverage and overall quality diff --git a/src/main/resources/wiki/Specification-v0.4.md b/src/main/resources/wiki/Specification-v0.4.md new file mode 100644 index 0000000..c490040 --- /dev/null +++ b/src/main/resources/wiki/Specification-v0.4.md @@ -0,0 +1,2112 @@ +# Naftiko Specification + +**Version 0.4** + +**Publication Date:** February 2026 + +--- + +- **Table of Contents** + +## 1. Introduction + +The Naftiko Specification defines a standard, language-agnostic interface for describing modular, composable capabilities. In short, a **capability** is a functional unit that consumes external APIs (sources) and exposes adapters that allow other systems to interact with it. + +A Naftiko capability focuses on declaring the **integration intent** — what a system needs to consume and what it exposes — rather than implementation details. This higher-level abstraction makes capabilities naturally suitable for AI-driven discovery, orchestration and integration use cases, and beyond. When properly defined, a capability can be discovered, orchestrated, validated and executed with minimal implementation logic. The specification enables description of: + +- **Consumed sources**: External APIs or services that the capability uses +- **Exposed adapters**: Server interfaces that the capability provides (HTTP, REST, etc.) +- **Orchestration**: How calls to consumed sources are combined and mapped to realize exposed functions +- **External references**: Variables and resources resolved from external sources + +### 1.1 Schema Access + +The JSON Schema for the Naftiko Specification is available in two forms: + +- **Raw file** — The schema source file is hosted on GitHub: [capability-schema.json](https://github.com/naftiko/framework/blob/main/src/main/resources/schemas/capability-schema.json) +- **Interactive viewer** — A human-friendly viewer is available at: [Schema Viewer](https://naftiko.github.io/schema-viewer/) + +### 1.2 Core Objects + +**Capability**: The central object that defines a modular functional unit with clear input/output contracts. + +**Consumes**: External sources (APIs, services) that the capability uses to realize its operations. + +**Exposes**: Server adapters that provide access to the capability's operations. + +**Resources**: API endpoints that group related operations. + +**Operations**: Individual HTTP operations (GET, POST, etc.) that can be performed on resources. + +**Namespace**: A unique identifier for consumed sources, used for routing and mapping with the expose layer. + +**ExternalRef**: A declaration of an external reference providing variables to the capability. Two variants: file-resolved (for development) and runtime-resolved (for production). Variables are explicitly declared via a `keys` map. + +### 1.3 Related Specifications. + + + +Three specifications that work better together. + +| | **OpenAPI** | **Arazzo** | **OpenCollections** | **Naftiko** | +| --- | --- | --- | --- | --- | +| **Focus** | Defines *what* your API is — the contract, the schema, the structure. | Defines *how* API calls are sequenced — the workflows between endpoints. | Defines *how* to use your API — the scenarios, the runnable collections. | Defines *what* a capability consumes and exposes — the integration intent. | +| **Scope** | Single API surface | Workflows across one or more APIs | Runnable collections of API calls | Modular capability spanning multiple APIs | +| **Key strengths** | ✓ Endpoints & HTTP methods
✓ Request/response schemas
✓ Authentication requirements
✓ Data types & validation
✓ SDK & docs generation | ✓ Multi-step sequences
✓ Step dependencies & data flow
✓ Success/failure criteria
✓ Reusable workflow definitions | ✓ Runnable, shareable collections
✓ Pre-request scripts & tests
✓ Environment variables
✓ Living, executable docs | ✓ Consume/expose duality
✓ Namespace-based routing
✓ Orchestration & forwarding
✓ AI-driven discovery
✓ Composable capabilities | +| **Analogy** | The *parts list* and dimensions | The *assembly sequence* between parts | The *step-by-step assembly guide* you can run | The *product blueprint* — what goes in, what comes out | +| **Best used when you need to…** | Define & document an API contract, generate SDKs, validate payloads | Describe multi-step API workflows with dependencies | Share runnable API examples, test workflows, onboard developers | Declare a composable capability that consumes sources and exposes unified interfaces | + +**OpenAPI** tells you the shape of the door. **Arazzo** describes the sequence of doors to walk through. **OpenCollections** lets you actually walk through them. **Naftiko** combines the features of those 3 specs into a single, coherent spec, reducing complexity and offering consistent tooling out of the box + +--- + +## 2. Format + +Naftiko specifications can be represented in YAML format, complying with the provided Naftiko schema which is made available in both JSON Schema and [JSON Structure](https://json-structure.org/) formats. + +All field names in the specification are **case-sensitive**. + +Naftiko Objects expose two types of fields: + +- **Fixed fields**: which have a declared name +- **Patterned fields**: which have a declared pattern for the field name + +--- + +## 3. Objects and Fields + +### 3.1 Naftiko Object + +This is the root object of the Naftiko document. + +#### 3.1.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **naftiko** | `string` | **REQUIRED**. Version of the Naftiko schema. MUST be `"0.4"` for this version. | +| **info** | `Info` | **REQUIRED**. Metadata about the capability. | +| **capability** | `Capability` | **REQUIRED**. Technical configuration of the capability including sources and adapters. | +| **externalRefs** | `ExternalRef[]` | List of external references for variable injection. Each entry declares injected variables via a `keys` map. | + +#### 3.1.2 Rules + +- The `naftiko` field MUST be present and MUST have the value `"0.4"` for documents conforming to this version of the specification. +- Both `info` and `capability` objects MUST be present. +- The `externalRefs` field is OPTIONAL. When present, it MUST contain at least one entry. +- No additional properties are allowed at the root level. + +--- + +### 3.2 Info Object + +Provides metadata about the capability. + +#### 3.2.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **label** | `string` | **REQUIRED**. The display name of the capability. | +| **description** | `string` | **REQUIRED**. A description of the capability. The more meaningful it is, the easier for agent discovery. | +| **tags** | `string[]` | List of tags to help categorize the capability for discovery and filtering. | +| **created** | `string` | Date the capability was created (format: `YYYY-MM-DD`). | +| **modified** | `string` | Date the capability was last modified (format: `YYYY-MM-DD`). | +| **stakeholders** | `Person[]` | List of stakeholders related to this capability (for discovery and filtering). | + +#### 3.2.2 Rules + +- Both `label` and `description` are mandatory. +- No additional properties are allowed. + +#### 3.2.3 Info Object Example + +```yaml +info: + label: Notion Page Creator + description: Creates and manages Notion pages with rich content formatting + tags: + - notion + - automation + created: "2026-02-17" + modified: "2026-02-17" + stakeholders: + - role: owner + fullName: "Jane Doe" + email: "jane.doe@example. +``` + +--- + +### 3.3 Person Object + +Describes a person related to the capability. + +#### 3.3.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **role** | `string` | **REQUIRED**. The role of the person in relation to the capability. E.g. owner, editor, viewer. | +| **fullName** | `string` | **REQUIRED**. The full name of the person. | +| **email** | `string` | The email address of the person. MUST match pattern `^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$`. | + +#### 3.3.2 Rules + +- Both `role` and `fullName` are mandatory. +- No additional properties are allowed. + +#### 3.3.3 Person Object Example + +```yaml +- role: owner + fullName: "Jane Doe" + email: "jane.doe@example.com" +``` + +--- + +### 3.4 Capability Object + +Defines the technical configuration of the capability. + +#### 3.4.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **exposes** | `Exposes[]` | **REQUIRED**. List of exposed server adapters. | +| **consumes** | `Consumes[]` | **REQUIRED**. List of consumed client adapters. | + +#### 3.4.2 Rules + +- The `exposes` array MUST contain at least one entry. +- The `consumes` array MUST contain at least one entry. +- Each `consumes` entry MUST include both `baseUri` and `namespace` fields. +- There are several types of exposed adapters and consumed sources objects, all will be described in following objects. +- No additional properties are allowed. + +#### 3.4.3 Namespace Uniqueness Rule + +When multiple `consumes` entries are present: + +- Each `namespace` value MUST be unique across all consumes entries. +- The `namespace` field is used for routing from the expose layer to the correct consumed source. +- Duplicate namespace values will result in ambiguous routing and are forbidden. + +#### 3.4.4 Capability Object Example + +```yaml +capability: + exposes: + - type: api + port: 3000 + namespace: tasks-api + resources: + - path: /tasks + description: "Endpoint to create tasks via the external API" + operations: + - method: POST + label: Create Task + call: api.create-task + outputParameters: + - type: string + mapping: $.taskId + consumes: + - type: http + namespace: api + baseUri: https://api.example.com + resources: + - name: tasks + label: Tasks API + path: /tasks + operations: + - name: create-task + label: Create Task + method: POST + inputParameters: + - name: task_id + in: path + outputParameters: + - name: taskId + type: string + value: $.data.id +``` + +--- + +### 3.5 Exposes Object + +Describes a server adapter that exposes functionality. + +> Update (schema v0.4): the exposition adapter is **API** with `type: "api"` (and a required `namespace`). Legacy `httpProxy` / `rest` exposition types are not part of the JSON Schema anymore. +> + +#### 3.5.1 API Expose + +API exposition configuration. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"api"`. | +| **address** | `string` | Server address. Can be a hostname, IPv4, or IPv6 address. | +| **port** | `integer` | **REQUIRED**. Port number. MUST be between 1 and 65535. | +| **authentication** | `Authentication` | Authentication configuration. | +| **namespace** | `string` | **REQUIRED**. Unique identifier for this exposed API. | +| **resources** | `ExposedResource[]` | **REQUIRED**. List of exposed resources. | + +#### 3.5.2 ExposedResource Object + +An exposed resource with **operations** and/or **forward** configuration. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **path** | `string` | **REQUIRED**. Path of the resource (supports `param` placeholders). | +| **description** | `string` | **REQUIRED**. Used to provide *meaningful* information about the resource. In a world of agents, context is king. | +| **name** | `string` | Technical name for the resource (used for references, pattern `^[a-zA-Z0-9-]+$`). | +| **label** | `string` | Display name for the resource (likely used in UIs). | +| **inputParameters** | `ExposedInputParameter[]` | Input parameters attached to the resource. | +| **operations** | `ExposedOperation[]` | Operations available on this resource. | +| **forward** | `ForwardConfig` | Forwarding configuration to a consumed namespace. | + +#### 3.5.3 Rules + +- Both `description` and `path` are mandatory. +- At least one of `operations` or `forward` MUST be present. Both can coexist on the same resource. +- if both `operations` or `forward` are present, in case of conflict, `operations` takes precendence on `forward`. +- No additional properties are allowed. + +#### 3.5.4 Address Validation Patterns + +- **Hostname**: `^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` +- **IPv4**: `^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` +- **IPv6**: `^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$` + +#### 3.5.5 Exposes Object Examples + +**API Expose with operations:** + +```yaml +type: api +port: 3000 +namespace: sample +resources: + - path: /status + description: "Health check endpoint" + name: health + operations: + - name: get-status + method: GET + call: api.health-check + outputParameters: + - type: string + mapping: $.status +``` + +**API Expose with forward:** + +```yaml +type: api +port: 8080 +namespace: proxy +resources: + - path: /notion/{path} + description: "Forward requests to the Notion API" + forward: + targetNamespace: notion + trustedHeaders: + - Notion-Version +``` + +**API Expose with both operations and forward:** + +```yaml +type: api +port: 9090 +namespace: hybrid +resources: + - path: /data/{path} + description: "Resource with orchestrated operations and pass-through forwarding" + operations: + - name: get-summary + method: GET + call: api.get-summary + forward: + targetNamespace: api + trustedHeaders: + - Authorization +``` + +--- + +### 3.6 Consumes Object + +Describes a client adapter for consuming external APIs. + +> Update (schema v0.4): `targetUri` is now `baseUri`. The `headers` field has been removed — use `inputParameters` with `in: "header"` instead. +> + +#### 3.6.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. Type of consumer. Valid values: `"http"`. | +| **namespace** | `string` | Path suffix used for routing from exposes. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **baseUri** | `string` | **REQUIRED**. Base URI for the consumed API. Must be a valid http(s) URL (no `path` placeholder in the schema). | +| **authentication** | Authentication Object | Authentication configuration. Defaults to `"inherit"`. | +| **description** | `string` | **REQUIRED**. A description of the consumed API. The more meaningful it is, the easier for agent discovery. | +| **inputParameters** | `ConsumedInputParameter[]` | Input parameters applied to all operations in this consumed API. | +| **resources** | [ConsumedHttpResource Object] | **REQUIRED**. List of API resources. | + +#### 3.6.2 Rules + +- The `type` field MUST be `"http"`. +- The `baseUri` field is required. +- The `namespace` field is required and MUST be unique across all consumes entries. +- The `namespace` value MUST match the pattern `^[a-zA-Z0-9-]+$` (alphanumeric and hyphens only). +- The `description` field is required. +- The `resources` array is required and MUST contain at least one entry. + +#### 3.6.3 Base URI Format + +The `baseUri` field MUST be a valid `http://` or `https://` URL, and may optionally include a base path. + +Example: `https://api.github.com` or `https://api.github.com/v3` + +#### 3.6.4 Consumes Object Example + +```yaml +type: http +namespace: github +baseUri: https://api.github.com +authentication: + type: bearer + token: "{{github_token}}" +inputParameters: + - name: Accept + in: header + value: "application/vnd.github.v3+json" +resources: + - name: users + label: Users API + path: /users/{username} + operations: + - name: get-user + label: Get User + method: GET + inputParameters: + - name: username + in: path + outputParameters: + - name: userId + type: string + value: $.id + - name: repos + label: Repositories API + path: /users/{username}/repos + operations: + - name: list-repos + label: List Repositories + method: GET + inputParameters: + - name: username + in: path + outputParameters: + - name: repos + type: array + value: $ +``` + +--- + +### 3.7 ConsumedHttpResource Object + +Describes an API resource that can be consumed from an external API. + +#### 3.7.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Technical name for the resource. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **label** | `string` | Display name of the resource. | +| **description** | `string` | Description of the resource. | +| **path** | `string` | **REQUIRED**. Path of the resource, relative to the consumes `baseUri`. | +| **inputParameters** | `ConsumedInputParameter[]` | Input parameters for this resource. | +| **operations** | `ConsumedHttpOperation[]` | **REQUIRED**. List of operations for this resource. | + +#### 3.7.2 Rules + +- The `name` field MUST be unique within the parent consumes object's resources array. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-]+$` (alphanumeric and hyphens only). +- The `path` field will be appended to the parent consumes object's `baseUri`. +- The `operations` array MUST contain at least one entry. +- No additional properties are allowed. + +#### 3.7.3 ConsumedHttpResource Object Example + +```yaml +name: users +label: Users API +path: /users/{username} +inputParameters: + - name: username + in: path +operations: + - name: get-user + label: Get User + method: GET + outputParameters: + - name: userId + type: string + value: $.id +``` + +--- + +### 3.8 ConsumedHttpOperation Object + +Describes an operation that can be performed on a consumed resource. + +#### 3.8.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Technical name for the operation. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **label** | `string` | Display name of the operation. | +| **description** | `string` | A longer description of the operation for documentation purposes. | +| **method** | `string` | **REQUIRED**. HTTP method. One of: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. Default: `GET`. | +| **inputParameters** | `ConsumedInputParameter[]` | Input parameters for the operation. | +| **outputRawFormat** | `string` | The raw format of the response. One of: `json`, `xml`, `avro`, `protobuf`, `csv`, `yaml` . Default: `json`. | +| **outputParameters** | `ConsumedOutputParameter[]` | Output parameters extracted from the response via JsonPath. | +| **body** | `RequestBody` | Request body configuration. | + +#### 3.8.2 Rules + +- The `name` field MUST be unique within the parent resource's operations array. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-]+$` (alphanumeric and hyphens only). +- Both `name` and `method` are mandatory. +- No additional properties are allowed. + +#### 3.8.3 ConsumedHttpOperation Object Example + +```yaml +name: get-user +label: Get User Profile +method: GET +inputParameters: + - name: username + in: path +outputParameters: + - name: userId + type: string + value: $.id + - name: username + type: string + value: $.login + - name: email + type: string + value: $.email +``` + +--- + +### 3.9 ExposedOperation Object + +Describes an operation exposed on an exposed resource. + +> Update (schema v0.4): ExposedOperation now supports two modes via `oneOf` — **simple** (direct call with mapped output) and **orchestrated** (multi-step with named operation). The `call` and `with` fields are new. The `name` and `steps` fields are only required in orchestrated mode. +> + +#### 3.9.1 Fixed Fields + +All fields available on ExposedOperation: + +| Field Name | Type | Description | +| --- | --- | --- | +| **method** | `string` | **REQUIRED**. HTTP method. One of: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. | +| **name** | `string` | Technical name for the operation (pattern `^[a-zA-Z0-9-]+$`). **REQUIRED in orchestrated mode only.** | +| **label** | `string` | Display name for the operation (likely used in UIs). | +| **description** | `string` | A longer description of the operation. Useful for agent discovery and documentation. | +| **inputParameters** | `ExposedInputParameter[]` | Input parameters attached to the operation. | +| **call** | `string` | **Simple mode only**. Direct reference to a consumed operation. Format: `{namespace}.{operationId}`. MUST match pattern `^[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+$`. | +| **with** | `WithInjector` | **Simple mode only**. Parameter injection for the called operation. | +| **outputParameters** (simple) | `MappedOutputParameter[]` | **Simple mode**. Output parameters mapped from the consumed operation response. | +| **steps** | `OperationStep[]` | **Orchestrated mode only. REQUIRED** (at least 1 step). Sequence of calls to consumed operations. | +| **outputParameters** (orchestrated) | `OrchestratedOutputParameter[]` | **Orchestrated mode**. Output parameters with name and type. | +| **mappings** | `StepOutputMapping[]` | **Orchestrated mode only**. Maps step outputs to the operation's output parameters at the operation level. | + +#### 3.9.2 Modes (oneOf) + +**Simple mode** — A direct call to a single consumed operation: + +- `call` is **REQUIRED** +- `with` is optional (inject parameters into the call) +- `outputParameters` are `MappedOutputParameter[]` (type + mapping) +- `steps` MUST NOT be present +- `name` is optional + +**Orchestrated mode** — A multi-step orchestration: + +- `name` is **REQUIRED** +- `steps` is **REQUIRED** (at least 1 entry) +- `mappings` is optional — maps step outputs to the operation's output parameters at the operation level +- `outputParameters` are `OrchestratedOutputParameter[]` (name + type) +- `call` and `with` MUST NOT be present + +#### 3.9.3 Rules + +- Exactly one of the two modes MUST be used (simple or orchestrated). +- In simple mode, `call` MUST follow the format `{namespace}.{operationId}` and reference a valid consumed operation. +- In orchestrated mode, the `steps` array MUST contain at least one entry. Each step references a consumed operation using `{namespace}.{operationName}`. +- The `method` field is always required regardless of mode. + +#### 3.9.4 ExposedOperation Object Examples + +**Simple mode (direct call):** + +```yaml +method: GET +label: Get User Profile +call: github.get-user +with: + username: $this.sample.username +outputParameters: + - type: string + mapping: $.login + - type: number + mapping: $.id +``` + +**Orchestrated mode (multi-step):** + +```yaml +name: get-db +method: GET +label: Get Database +inputParameters: + - name: database_id + in: path + type: string + description: The ID of the database to retrieve +steps: + - type: call + name: fetch-db + call: notion.get-database + with: + database_id: "$this.sample.database_id" +mappings: + - targetName: db_name + value: "$.dbName" +outputParameters: + - name: db_name + type: string + - name: Api-Version + type: string +``` + +--- + +### 3.10 RequestBody Object + +Describes request body configuration for consumed operations. `RequestBody` is a `oneOf` — exactly one of five subtypes must be used. + +#### 3.10.1 Subtypes + +**RequestBodyJson** — JSON body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"json"`. | +| **data** | `string` | `object` | `array` | **REQUIRED**. The JSON payload. Can be a raw JSON string, an inline object, or an array. | + +**RequestBodyText** — Plain text, XML, or SPARQL body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. One of: `"text"`, `"xml"`, `"sparql"`. | +| **data** | `string` | **REQUIRED**. The text payload. | + +**RequestBodyFormUrlEncoded** — URL-encoded form body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"formUrlEncoded"`. | +| **data** | `string` | `object` | **REQUIRED**. Either a raw URL-encoded string or an object whose values are strings. | + +**RequestBodyMultipartForm** — Multipart form body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"multipartForm"`. | +| **data** | `RequestBodyMultipartFormPart[]` | **REQUIRED**. Array of form parts. Each part has: `name` (required), `value` (required), `filename` (optional), `contentType` (optional). | + +**RequestBodyRaw** — Raw string body + +`RequestBody` can also be a plain `string`. When used with a YAML block scalar (`|`), the string is sent as-is. Interpreted as JSON by default. + +#### 3.10.2 Rules + +- Exactly one of the five subtypes must be used. +- For structured subtypes, both `type` and `data` are mandatory. +- No additional properties are allowed on any subtype. + +#### 3.10.3 RequestBody Examples + +**JSON body (object):** + +```yaml +body: + type: json + data: + hello: "world" +``` + +**JSON body (string):** + +```yaml +body: + type: json + data: '{"key": "value"}' +``` + +**Text body:** + +```yaml +body: + type: text + data: "Hello, world!" +``` + +**Form URL-encoded body:** + +```yaml +body: + type: formUrlEncoded + data: + username: "admin" + password: "secret" +``` + +**Multipart form body:** + +```yaml +body: + type: multipartForm + data: + - name: "file" + value: "base64content..." + filename: "document.pdf" + contentType: "application/pdf" + - name: "description" + value: "My uploaded file" +``` + +**Raw body:** + +```yaml +body: | + { + "filter": { + "property": "Status", + "select": { "equals": "Active" } + } + } +``` + +--- + +### 3.11 InputParameter Objects + +> Update (schema v0.4): The single `InputParameter` object has been split into two distinct types: **ConsumedInputParameter** (used in consumes) and **ExposedInputParameter** (used in exposes, with additional `type` and `description` fields required). +> + +#### 3.11.1 ConsumedInputParameter Object + +Used in consumed resources and operations. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Parameter name. MUST match pattern `^[a-zA-Z0-9-*]+$`. | +| **in** | `string` | **REQUIRED**. Parameter location. Valid values: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. | +| **value** | `string` | Value or JSONPath reference. | + +**Rules:** + +- Both `name` and `in` are mandatory. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-*]+$`. +- The `in` field MUST be one of: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. +- A unique parameter is defined by the combination of `name` and `in`. +- No additional properties are allowed. + +**ConsumedInputParameter Example:** + +```yaml +- name: username + in: path +- name: page + in: query +- name: Authorization + in: header + value: "Bearer token" +``` + +#### 3.11.2 ExposedInputParameter Object + +Used in exposed resources and operations. Extends the consumed variant with `type` and `description` (both required) for agent discoverability, plus an optional `pattern` for validation. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Parameter name. MUST match pattern `^[a-zA-Z0-9-*]+$`. | +| **in** | `string` | **REQUIRED**. Parameter location. Valid values: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. | +| **type** | `string` | **REQUIRED**. Data type of the parameter. One of: `string`, `number`, `boolean`, `object`, `array`. | +| **description** | `string` | **REQUIRED**. Human-readable description of the parameter. Essential for agent discovery. | +| **pattern** | `string` | Optional regex pattern for parameter value validation. | +| **value** | `string` | Default value or JSONPath reference. | + +**Rules:** + +- All of `name`, `in`, `type`, and `description` are mandatory. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-*]+$`. +- The `in` field MUST be one of: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. +- The `type` field MUST be one of: `"string"`, `"number"`, `"boolean"`, `"object"`, `"array"`. +- No additional properties are allowed. + +**ExposedInputParameter Example:** + +```yaml +- name: database_id + in: path + type: string + description: The unique identifier of the Notion database + pattern: "^[a-f0-9-]+$" +- name: page_size + in: query + type: number + description: Number of results per page (max 100) +``` + +--- + +### 3.12 OutputParameter Objects + +> Update (schema v0.4): The single `OutputParameter` object has been split into three distinct types: **ConsumedOutputParameter** (used in consumed operations), **MappedOutputParameter** (used in simple-mode exposed operations), and **OrchestratedOutputParameter** (used in orchestrated-mode exposed operations). +> + +#### 3.12.1 ConsumedOutputParameter Object + +Used in consumed operations to extract values from the raw API response. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Output parameter name. MUST match pattern `^[a-zA-Z0-9-_]+$`. | +| **type** | `string` | **REQUIRED**. Data type. One of: `string`, `number`, `boolean`, `object`, `array`. | +| **value** | `string` | **REQUIRED**. JsonPath expression to extract value from consumed function response. | + +**Rules:** + +- All three fields (`name`, `type`, `value`) are mandatory. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-_*]+$`. +- The `value` field MUST start with `$`. +- No additional properties are allowed. + +**ConsumedOutputParameter Example:** + +```yaml +outputParameters: + - name: dbName + type: string + value: $.title[0].text.content + - name: dbId + type: string + value: $.id +``` + +#### 3.12.2 MappedOutputParameter Object + +Used in **simple mode** exposed operations. Maps a value from the consumed response using `type` and `mapping`. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. Data type. One of: `string`, `number`, `boolean`, `object`, `array`. | +| **mapping** | `string` | `object` | **REQUIRED**. For scalar types (`string`, `number`, `boolean`): a JsonPath string. For `object`: an object with `properties` (recursive MappedOutputParameter map). For `array`: an object with `items` (recursive MappedOutputParameter). | + +**Subtypes by type:** + +- **`string`**, **`number`**, **`boolean`**: `mapping` is a JsonPath string (e.g. `$.login`) +- **`object`**: `mapping` is `{ properties: { key: MappedOutputParameter, ... } }` — recursive +- **`array`**: `mapping` is `{ items: MappedOutputParameter }` — recursive + +**Rules:** + +- Both `type` and `mapping` are mandatory. +- No additional properties are allowed. + +**MappedOutputParameter Examples:** + +```yaml +# Scalar mapping +outputParameters: + - type: string + mapping: $.login + - type: number + mapping: $.id + +# Object mapping (recursive) +outputParameters: + - type: object + mapping: + properties: + username: + type: string + mapping: $.login + userId: + type: number + mapping: $.id + +# Array mapping (recursive) +outputParameters: + - type: array + mapping: + items: + type: object + mapping: + properties: + name: + type: string + mapping: $.name +``` + +#### 3.12.3 OrchestratedOutputParameter Object + +Used in **orchestrated mode** exposed operations. Declares an output by `name` and `type` (the value is populated via step mappings). + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Output parameter name. | +| **type** | `string` | **REQUIRED**. Data type. One of: `string`, `number`, `boolean`, `object`, `array`. | + +**Subtypes by type:** + +- **`string`**, **`number`**, **`boolean`** (scalar): only `name` and `type` +- **`array`**: adds `items` (recursive OrchestratedOutputParameter without `name`) +- **`object`**: adds `properties` (map of name → recursive OrchestratedOutputParameter without `name`) + +**Rules:** + +- Both `name` and `type` are mandatory. +- No additional properties are allowed. + +**OrchestratedOutputParameter Example:** + +```yaml +outputParameters: + - name: db_name + type: string + - name: Api-Version + type: string + - name: results + type: array + items: + type: object + properties: + id: + type: string + title: + type: string +``` + +#### 3.12.4 JsonPath roots (extensions) + +In a consumed resource, **`$`** refers to the *raw response payload* of the consumed operation (after decoding based on `outputRawFormat`). The root `$` gives direct access to the JSON response body. + +Example, if you consider the following JSON response : + +```json +{ + "id": "154548", + "titles": [ + { + "text": { + "content": "This is title[0].text.content", + "author": "user1" + } + } + ], + "created_time": "2024-06-01T12:00:00Z" +} +``` + +- `$.id` is `154548` +- `$.titles[0].text.content` is `This is title[0].text.content` + +#### 3.12.5 Common patterns + +- `$.fieldName` — accesses a top-level field +- `$.data.user.id` — accesses nested fields +- `$.items[0]` — accesses array elements +- `$.items[*].id` — accesses all ids in an array + +--- + +### 3.13 OperationStep Object + +Describes a single step in an orchestrated operation. `OperationStep` is a `oneOf` between two subtypes: **OperationStepCall** and **OperationStepLookup**, both sharing a common **OperationStepBase**. + +> Update (schema v0.4): OperationStep is now a discriminated union (`oneOf`) with a required `type` field (`"call"` or `"lookup"`) and a required `name` field. `OperationStepCall` uses `with` (WithInjector) instead of `inputParameters`. `OperationStepLookup` is entirely new. +> + +#### 3.13.1 OperationStepBase (shared fields) + +All operation steps share these base fields: + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. Step type discriminator. One of: `"call"`, `"lookup"`. | +| **name** | `string` | **REQUIRED**. Technical name for the step (pattern `^[a-zA-Z0-9-]+$`). Used as namespace for referencing step outputs in mappings and expressions. | + +#### 3.13.2 OperationStepCall + +Calls a consumed operation. + +**Fixed Fields** (in addition to base): + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"call"`. | +| **name** | `string` | **REQUIRED**. Step name (from base). | +| **call** | `string` | **REQUIRED**. Reference to consumed operation. Format: `{namespace}.{operationId}`. MUST match pattern `^[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+$`. | +| **with** | `WithInjector` | Parameter injection for the called operation. Keys are parameter names, values are strings or numbers (static values or `$this` references). | + +**Rules:** + +- `type`, `name`, and `call` are mandatory. +- The `call` field MUST follow the format `{namespace}.{operationName}`. +- The `namespace` portion MUST correspond to a namespace defined in one of the capability's consumes entries. +- The `operationName` portion MUST correspond to an operation `name` defined in the consumes entry identified by the namespace. +- `with` uses the same `WithInjector` object as simple-mode ExposedOperation (see §3.18). +- No additional properties are allowed. + +#### 3.13.3 OperationStepLookup + +Performs a lookup against the output of a previous call step, matching values and extracting fields. + +**Fixed Fields** (in addition to base): + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"lookup"`. | +| **name** | `string` | **REQUIRED**. Step name (from base). | +| **index** | `string` | **REQUIRED**. Name of a previous call step whose output serves as the lookup table. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **match** | `string` | **REQUIRED**. Name of the key field in the index to match against. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **lookupValue** | `string` | **REQUIRED**. JsonPath expression resolving to the value(s) to look up. | +| **outputParameters** | `string[]` | **REQUIRED**. List of field names to extract from the matched index entries (minimum 1 entry). | + +**Rules:** + +- `type`, `name`, `index`, `match`, `lookupValue`, and `outputParameters` are all mandatory. +- `outputParameters` MUST contain at least one entry. +- The `index` value MUST reference the `name` of a previous `call` step in the same orchestration. +- No additional properties are allowed. + +#### 3.13.4 Call Reference Resolution + +The `call` value on an `OperationStepCall` is resolved as follows: + +1. Split the value on the `.` character into namespace and operationName +2. Find the consumes entry with matching `namespace` field +3. Within that consumes entry's resources, find the operation with matching `name` field +4. Execute that operation as part of the orchestration sequence + +#### 3.13.5 OperationStep Object Examples + +**Call step with parameter injection:** + +```yaml +steps: + - type: call + name: fetch-db + call: notion.get-database + with: + database_id: $this.sample.database_id +``` + +**Lookup step (match against a previous call's output):** + +```yaml +steps: + - type: call + name: list-users + call: github.list-users + - type: lookup + name: find-user + index: list-users + match: email + lookupValue: $this.sample.user_email + outputParameters: + - login + - id +``` + +**Multi-step orchestration (call + lookup):** + +```yaml +steps: + - type: call + name: get-entries + call: api.list-entries + - type: lookup + name: resolve-entry + index: get-entries + match: entry_id + lookupValue: $this.sample.target_id + outputParameters: + - title + - status + - type: call + name: post-result + call: slack.post-message + with: + text: $this.sample.title +``` + +--- + +### 3.14 StepOutputMapping Object + +Describes how to map the output of an operation step to the input of another step or to the output of the exposed operation. + +#### 3.14.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **targetName** | `string` | **REQUIRED**. The name of the parameter to map to. It can be an input parameter of a next step or an output parameter of the exposed operation. | +| **value** | `string` | **REQUIRED**. A JsonPath reference to the value to map from. E.g. `$.get-database.database_id`. | + +#### 3.14.2 Rules + +- Both `targetName` and `value` are mandatory. +- No additional properties are allowed. + +#### 3.14.3 How mappings wire steps to exposed outputs + +A StepOutputMapping connects the **output parameters of a consumed operation** (called by the step) to the **output parameters of the exposed operation** (or to input parameters of subsequent steps). + +- **`targetName`** — refers to the `name` of an output parameter declared on the exposed operation, or the `name` of an input parameter of a subsequent step. The target parameter receives its value from the mapping. +- **`value`** — a JsonPath expression where **`$`** is the root of the consumed operation's output parameters. The syntax `$.{outputParameterName}` references a named output parameter of the consumed operation called in this step. + +#### 3.14.4 End-to-end example + +Consider a consumed operation `notion.get-database` that declares: + +```yaml +# In consumes → resources → operations +name: "get-database" +outputParameters: + - name: "dbName" + value: "$.title[0].text.content" +``` + +And the exposed side of the capability: + +```yaml +# In exposes +exposes: + - type: "api" + address: "localhost" + port: 9090 + namespace: "sample" + resources: + - path: "/databases/{database_id}" + name: "db" + label: "Database resource" + description: "Retrieve information about a Notion database" + inputParameters: + - name: "database_id" + in: "path" + type: "string" + description: "The unique identifier of the Notion database" + operations: + - name: "get-db" + method: "GET" + label: "Get Database" + outputParameters: + - name: "db_name" + type: "string" + steps: + - type: "call" + name: "fetch-db" + call: "notion.get-database" + with: + database_id: "$this.sample.database_id" + mappings: + - targetName: "db_name" + value: "$.dbName" +``` + +Here is what happens at orchestration time: + +1. The step `fetch-db` calls `notion.get-database`, which extracts `dbName` and `dbId` from the raw response via its own output parameters. +2. The `with` injector passes `database_id` from the exposed input parameter (`$this.sample.database_id`) to the consumed operation. +3. The mapping `targetName: "db_name"` refers to the exposed operation's output parameter `db_name`. +4. The mapping `value: "$.dbName"` resolves to the value of the consumed operation's output parameter named `dbName`. +5. As a result, the exposed output `db_name` is populated with the value extracted by `$.dbName` (i.e. `title[0].text.content` from the raw Notion API response). + +#### 3.14.5 StepOutputMapping Object Example + +```yaml +mappings: + - targetName: "db_name" + value: "$.dbName" +``` + +--- + +### 3.15 `$this` Context Reference + +Describes how `$this` references work in `with` (WithInjector) and other expression contexts. + +> Update (schema v0.4): The former `OperationStepParameter` object (with `name` and `value` fields) has been replaced by `WithInjector` (see §3.18). This section now documents the `$this` expression root, which is used within `WithInjector` values. +> + +#### 3.15.1 The `$this` root + +In a `with` (WithInjector) value — whether on an ExposedOperation (simple mode) or an OperationStepCall — the **`$this`** root references the *current capability execution context*, i.e. values already resolved during orchestration. + +**`$this`** navigates the expose layer's input parameters using the path `$this.{exposeNamespace}.{inputParameterName}`. This allows a step or a simple-mode call to receive values that were provided by the caller of the exposed operation. + +- **`$this.{exposeNamespace}.{paramName}`** — accesses an input parameter of the exposed resource or operation identified by its namespace. +- The `{exposeNamespace}` corresponds to the `namespace` of the exposed API. +- The `{paramName}` corresponds to the `name` of an input parameter declared on the exposed resource or operation. + +#### 3.15.2 Example + +If the exposed API has namespace `sample` and an input parameter `database_id` declared on its resource, then: + +- `$this.sample.database_id` resolves to the value of `database_id` provided by the caller. + +**Usage in a WithInjector:** + +```yaml +call: notion.get-database +with: + database_id: $this.sample.database_id +``` + +--- + +### 3.16 Authentication Object + +Defines authentication configuration. Four types are supported: basic, apikey, bearer, and digest. + +#### 3.16.1 Basic Authentication + +HTTP Basic Authentication. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"basic"`. | +| **username** | `string` | Username for basic auth. | +| **password** | `string` | Password for basic auth. | + +**Example:** + +```yaml +authentication: + type: basic + username: admin + password: "secret_password" +``` + +#### 3.16.2 API Key Authentication + +API Key authentication via header or query parameter. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"apikey"`. | +| **key** | `string` | API key name (header name or query parameter name). | +| **value** | `string` | API key value. | +| **placement** | `string` | Where to place the key. Valid values: `"header"`, `"query"`. | + +**Example:** + +```yaml +authentication: + type: apikey + key: X-API-Key + value: "{{api_key}}" + placement: header +``` + +#### 3.16.3 Bearer Token Authentication + +Bearer token authentication. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"bearer"`. | +| **token** | `string` | Bearer token value. | + +**Example:** + +```yaml +authentication: + type: bearer + token: "bearer_token" +``` + +#### 3.16.4 Digest Authentication + +HTTP Digest Authentication. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"digest"`. | +| **username** | `string` | Username for digest auth. | +| **password** | `string` | Password for digest auth. | + +**Example:** + +```yaml +authentication: + type: digest + username: admin + password: "secret_password" +``` + +#### 3.16.5 Rules + +- Only one authentication type can be used per authentication object. +- The `type` field determines which additional fields are required or allowed. +- Authentication can be specified at multiple levels (exposes, consumes) with inner levels overriding outer levels. + +--- + +### 3.17 ForwardConfig Object + +Defines forwarding configuration for an exposed resource to pass requests through to a consumed namespace. + +> Update (schema v0.4): Renamed from `ForwardHeaders` to `ForwardConfig`. The `targetNamespaces` array has been replaced by a single `targetNamespace` string. +> + +#### 3.17.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **targetNamespace** | `string` | **REQUIRED**. The consumer namespace to forward requests to. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **trustedHeaders** | [`string`] | **REQUIRED**. List of headers allowed to be forwarded (minimum 1 entry). No wildcards supported. | + +#### 3.17.2 Rules + +- The `targetNamespace` field is mandatory and MUST reference a valid namespace from one of the capability's consumes entries. +- The `trustedHeaders` array is mandatory and MUST contain at least one entry. +- Header names in `trustedHeaders` are case-insensitive (following HTTP header conventions). +- Only headers listed in `trustedHeaders` will be forwarded to the consumed source. +- No additional properties are allowed. + +#### 3.17.3 ForwardConfig Object Example + +```yaml +forward: + targetNamespace: notion + trustedHeaders: + - Authorization + - Notion-Version +``` + +--- + +### 3.18 WithInjector Object + +Defines parameter injection for simple-mode exposed operations. Used with the `with` field on an ExposedOperation to inject values into the called consumed operation. + +> New in schema v0.4. +> + +#### 3.18.1 Shape + +`WithInjector` is an object whose keys are parameter names and whose values are static values or `$this` references. + +- Each key corresponds to a parameter `name` in the consumed operation's `inputParameters`. +- Each value is a `string` or a `number`: either a static value or a `$this.{namespace}.{paramName}` reference. + +#### 3.18.2 Rules + +- The keys MUST correspond to valid parameter names in the consumed operation being called. +- Values can be strings or numbers. +- String values can use the `$this` root to reference exposed input parameters (same as in OperationStepParameter). +- No additional constraints. + +#### 3.18.3 WithInjector Object Example + +```yaml +call: github.get-user +with: + username: $this.sample.username + Accept: "application/json" + maxRetries: 3 +``` + +--- + +### 3.19 ExternalRef Object + +> **Updated**: ExternalRef is now a discriminated union (`oneOf`) with two variants — **file-resolved** (for local development) and **runtime-resolved** (for production). Variables are explicitly declared via a `keys` map. +> + +Declares an external reference that provides variables to the capability. External references are declared at the root level of the Naftiko document via the `externalRefs` array. + +`ExternalRef` is a `oneOf` — exactly one of the two variants must be used. + +#### 3.19.1 File-Resolved ExternalRef + +Loads variables from a local file. Intended for **local development only**. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Unique identifier (kebab-case). MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **description** | `string` | **REQUIRED**. Used to provide *meaningful* information about the external reference. In a world of agents, context is king. | +| **type** | `string` | **REQUIRED**. MUST be `"environment"`. | +| **resolution** | `string` | **REQUIRED**. MUST be `"file"`. | +| **uri** | `string` | **REQUIRED**. URI pointing to the file (e.g. `file:///path/to/env.json`). | +| **keys** | `ExternalRefKeys` | **REQUIRED**. Map of variable names to keys in the resolved file content. | + +**Rules:** + +- All fields (`name`, `description`, `type`, `resolution`, `uri`, `keys`) are mandatory. +- No additional properties are allowed. + +#### 3.19.2 Runtime-Resolved ExternalRef + +Variables are injected by the execution environment at startup (default). The capability document does **not** specify where the values come from — this is delegated to the deployment platform. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Unique identifier (kebab-case). MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **type** | `string` | **REQUIRED**. MUST be `"environment"`. | +| **resolution** | `string` | **REQUIRED.** MUST be `"runtime"`. | +| **keys** | `ExternalRefKeys` | **REQUIRED**. Map of variable names to keys in the runtime context. | + +**Rules:** + +- `name`, `type`, and `keys` are mandatory. +- `resolution` is optional; when present MUST be `"runtime"`. +- No additional properties are allowed. + +Typical production providers include: + +- **HashiCorp Vault** — centralized secrets management +- **Kubernetes Secrets** / **ConfigMaps** — native K8s secret injection +- **AWS Secrets Manager** / **AWS SSM Parameter Store** +- **Azure Key Vault** +- **GCP Secret Manager** +- **Docker Secrets** — for containerized deployments +- **CI/CD pipeline variables** (GitHub Actions secrets, GitLab CI variables, etc.) + +#### 3.19.3 ExternalRefKeys Object + +A map of key-value pairs that define the variables to be injected from the external reference. + +- Each **key** is the variable name used for injection (available as `\{\{key\}\}` in the capability definition) +- Each **value** is the corresponding key in the resolved file content or runtime context + +Example: `{"notion_token": "NOTION_INTEGRATION_TOKEN"}` means the value of `NOTION_INTEGRATION_TOKEN` in the source will be injected as `{{notion_token}}` in the capability definition. + +**Schema:** + +```json +{ + "type": "object", + "additionalProperties": { "type": "string" } +} +``` + +#### 3.19.4 Rules + +- Each `name` value MUST be unique across all `externalRefs` entries. +- The `name` value MUST NOT collide with any `consumes` namespace to avoid ambiguity. +- The `keys` map MUST contain at least one entry. +- No additional properties are allowed on either variant. + + + +#### 3.19.5 ExternalRef Object Examples + +**File resolution (development):** + +```yaml +externalRefs: + - name: "notion-env" + type: "environment" + description: "External reference to Notion API for accessing project data stored in Notion." + resolution: file + uri: "file:///path/to/notion_env.json" + keys: + notion_token: "NOTION_INTEGRATION_TOKEN" + notion_projects_db_id: "PROJECTS_DATABASE_ID" + notion_time_tracker_db_id: "TIME_TRACKER_DATABASE_ID" +``` + +**Runtime resolution (production):** + +```yaml +externalRefs: + - name: "secrets" + type: "environment" + resolution: runtime + keys: + notion_token: "NOTION_INTEGRATION_TOKEN" + github_token: "GITHUB_TOKEN" +``` + +**Minimal runtime (resolution omitted — defaults to runtime):** + +```yaml +externalRefs: + - name: "env" + type: "environment" + keys: + api_key: "API_KEY" +``` + +--- + +### 3.20 Expression Syntax + +Variables declared in `externalRefs` via the `keys` map are injected into the capability document using mustache-style `\{\{variable\}\}` expressions. + +#### 3.20.1 Format + +The expression format is `\{\{key\}\}`, where `key` is a variable name declared in the `keys` map of an `externalRefs` entry. + +Expressions can appear in any `string` value within the document, including authentication tokens, header values, and input parameter values. + +#### 3.20.2 Resolution + +At runtime, expressions are resolved as follows: + +1. Find the `externalRefs` entry whose `keys` map contains the referenced variable name +2. Look up the corresponding source key in the `keys` map +3. Resolve the source key value using the strategy defined by `resolution` (`file` lookup or `runtime` injection) +4. Replace the `\{\{variable\}\}` expression with the resolved value + +If a referenced variable is not declared in any `externalRefs` entry's `keys`, the document MUST be considered invalid. + +#### 3.20.3 Relationship with `$this` + +`\{\{variable\}\}` expressions and `$this` references serve different purposes: + +- `\{\{variable\}\}` resolves **static configuration** from external references (secrets, environment variables) declared via `keys` +- `$this.{exposeNamespace}.{paramName}` resolves **runtime orchestration** values from the expose layer's input parameters + +The two expression systems are independent and MUST NOT be mixed. + +#### 3.20.4 Expression Examples + +```yaml +# Authentication token from external ref +authentication: + type: bearer + token: "{{notion_token}}" + +# Input parameter with header value from external ref +inputParameters: + - name: Authorization + in: header + value: "Bearer {{api_key}}" + +# Corresponding externalRefs declaration +externalRefs: + - name: "env" + type: "environment" + keys: + notion_token: "NOTION_TOKEN" + api_key: "API_KEY" +``` + +--- + +## 4. Complete Examples + +This section provides progressive examples — from the simplest capability to a full-featured one — to illustrate the main patterns of the specification. All examples are pseudo-functional and use realistic API shapes. + +### 4.1 Forward-only capability (proxy) + +The simplest capability: forward incoming requests to a consumed API without any transformation. + +```yaml +--- +naftiko: "0.4" +info: + label: "Notion Proxy" + description: "Pass-through proxy to the Notion API for development and debugging" + tags: + - proxy + - notion + created: "2026-02-01" + modified: "2026-02-01" + +capability: + exposes: + - type: "api" + port: 8080 + namespace: "proxy" + resources: + - path: "/notion/{path}" + description: "Forwards all requests to the Notion API" + forward: + targetNamespace: "notion" + trustedHeaders: + - "Authorization" + - "Notion-Version" + + consumes: + - type: "http" + namespace: "notion" + description: "Notion public API" + baseUri: "https://api.notion.com/v1" + resources: + - name: "all" + path: "/{path}" + operations: + - name: "any" + method: "GET" +``` + +### 4.2 Simple-mode capability (direct call) + +A single exposed operation that directly calls a consumed operation, maps parameters with `with`, and extracts output. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + github_token: "GITHUB_TOKEN" +info: + label: "GitHub User Lookup" + description: "Exposes a simplified endpoint to retrieve GitHub user profiles" + tags: + - github + - users + created: "2026-02-01" + modified: "2026-02-01" + +capability: + exposes: + - type: "api" + port: 3000 + namespace: "app" + resources: + - path: "/users/{username}" + description: "Look up a GitHub user by username" + name: "user" + inputParameters: + - name: "username" + in: "path" + type: "string" + description: "The GitHub username to look up" + operations: + - method: "GET" + label: "Get User" + call: "github.get-user" + with: + username: "$this.app.username" + outputParameters: + - type: "string" + mapping: "$.login" + - type: "string" + mapping: "$.email" + - type: "number" + mapping: "$.id" + + consumes: + - type: "http" + namespace: "github" + description: "GitHub REST API v3" + baseUri: "https://api.github.com" + authentication: + type: "bearer" + token: "{{github_token}}" + resources: + - name: "users" + path: "/users/{username}" + label: "Users" + operations: + - name: "get-user" + label: "Get User" + method: "GET" + inputParameters: + - name: "username" + in: "path" + outputParameters: + - name: "login" + type: "string" + value: "$.login" + - name: "email" + type: "string" + value: "$.email" + - name: "id" + type: "number" + value: "$.id" +``` + +### 4.3 Orchestrated capability (multi-step call) + +An exposed operation that chains two consumed operations using named steps and `with`. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + notion_token: "NOTION_TOKEN" +info: + label: "Database Inspector" + description: "Retrieves a Notion database then queries its contents in a single exposed operation" + tags: + - notion + - orchestration + created: "2026-02-10" + modified: "2026-02-10" + +capability: + exposes: + - type: "api" + port: 9090 + namespace: "inspector" + resources: + - path: "/databases/{database_id}/summary" + description: "Returns database metadata and first page of results" + name: "db-summary" + inputParameters: + - name: "database_id" + in: "path" + type: "string" + description: "The Notion database ID" + operations: + - name: "get-summary" + method: "GET" + label: "Get Database Summary" + steps: + - type: "call" + name: "fetch-db" + call: "notion.get-database" + with: + database_id: "$this.inspector.database_id" + - type: "call" + name: "query-db" + call: "notion.query-database" + with: + database_id: "$this.inspector.database_id" + mappings: + - targetName: "db_name" + value: "$.fetch-db.dbName" + - targetName: "row_count" + value: "$.query-db.resultCount" + outputParameters: + - name: "db_name" + type: "string" + - name: "row_count" + type: "number" + + consumes: + - type: "http" + namespace: "notion" + description: "Notion public API" + baseUri: "https://api.notion.com/v1" + authentication: + type: "bearer" + token: "{{notion_token}}" + inputParameters: + - name: "Notion-Version" + in: "header" + value: "2022-06-28" + resources: + - name: "databases" + path: "/databases/{database_id}" + label: "Databases" + operations: + - name: "get-database" + label: "Get Database" + method: "GET" + inputParameters: + - name: "database_id" + in: "path" + outputParameters: + - name: "dbName" + type: "string" + value: "$.title[0].text.content" + - name: "dbId" + type: "string" + value: "$.id" + - name: "queries" + path: "/databases/{database_id}/query" + label: "Database queries" + operations: + - name: "query-database" + label: "Query Database" + method: "POST" + inputParameters: + - name: "database_id" + in: "path" + outputParameters: + - name: "resultCount" + type: "number" + value: "$.results.length()" + - name: "results" + type: "array" + value: "$.results" +``` + +### 4.4 Orchestrated capability with lookup step + +Demonstrates a `lookup` step that cross-references the output of a previous call to enrich data. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + hr_api_key: "HR_API_KEY" +info: + label: "Team Member Resolver" + description: "Resolves team member details by matching email addresses from a project tracker" + tags: + - hr + - lookup + created: "2026-02-15" + modified: "2026-02-15" + +capability: + exposes: + - type: "api" + port: 4000 + namespace: "team" + resources: + - path: "/resolve/{email}" + description: "Finds a team member by email and returns their profile" + name: "resolve" + inputParameters: + - name: "email" + in: "path" + type: "string" + description: "Email address to look up" + operations: + - name: "resolve-member" + method: "GET" + label: "Resolve Team Member" + steps: + - type: "call" + name: "list-members" + call: "hr.list-employees" + - type: "lookup" + name: "find-member" + index: "list-members" + match: "email" + lookupValue: "$this.team.email" + outputParameters: + - "fullName" + - "department" + - "role" + mappings: + - targetName: "name" + value: "$.find-member.fullName" + - targetName: "department" + value: "$.find-member.department" + - targetName: "role" + value: "$.find-member.role" + outputParameters: + - name: "name" + type: "string" + - name: "department" + type: "string" + - name: "role" + type: "string" + + consumes: + - type: "http" + namespace: "hr" + description: "Internal HR system API" + baseUri: "https://hr.internal.example.com/api" + authentication: + type: "apikey" + key: "X-Api-Key" + value: "{{hr_api_key}}" + placement: "header" + resources: + - name: "employees" + path: "/employees" + label: "Employees" + operations: + - name: "list-employees" + label: "List All Employees" + method: "GET" + outputParameters: + - name: "email" + type: "string" + value: "$.items[*].email" + - name: "fullName" + type: "string" + value: "$.items[*].name" + - name: "department" + type: "string" + value: "$.items[*].department" + - name: "role" + type: "string" + value: "$.items[*].role" +``` + +### 4.5 Full-featured capability (mixed modes) + +Combines forward proxy, simple-mode operations, orchestrated multi-step with lookup, and multiple consumed sources. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + notion_token: "NOTION_TOKEN" + github_token: "GITHUB_TOKEN" +info: + label: "Project Dashboard" + description: "Aggregates project data from Notion and GitHub into a unified API, with a pass-through proxy for direct access" + tags: + - dashboard + - notion + - github + created: "2026-02-20" + modified: "2026-02-20" + stakeholders: + - role: "owner" + fullName: "Jane Doe" + email: "jane.doe@example.com" + - role: "editor" + fullName: "John Smith" + email: "john.smith@example.com" + +capability: + exposes: + - type: "api" + port: 9090 + namespace: "dashboard" + resources: + # --- Forward proxy (simplest) --- + - path: "/github/{path}" + description: "Direct pass-through to the GitHub API for debugging" + forward: + targetNamespace: "github" + trustedHeaders: + - "Authorization" + + # --- Simple mode (direct call) --- + - path: "/repos/{owner}/{repo}" + description: "Retrieve a GitHub repository summary" + name: "repo" + inputParameters: + - name: "owner" + in: "path" + type: "string" + description: "Repository owner (user or organization)" + - name: "repo" + in: "path" + type: "string" + description: "Repository name" + operations: + - method: "GET" + label: "Get Repository" + call: "github.get-repo" + with: + owner: "$this.dashboard.owner" + repo: "$this.dashboard.repo" + outputParameters: + - type: "string" + mapping: "$.full_name" + - type: "number" + mapping: "$.stargazers_count" + - type: "string" + mapping: "$.language" + + # --- Orchestrated mode (multi-step call + lookup) --- + - path: "/projects/{database_id}/contributors" + description: "Lists project tasks from Notion and enriches each assignee with GitHub profile data" + name: "contributors" + inputParameters: + - name: "database_id" + in: "path" + type: "string" + description: "Notion database ID for the project tracker" + operations: + - name: "list-contributors" + method: "GET" + label: "List Project Contributors" + steps: + - type: "call" + name: "query-tasks" + call: "notion.query-database" + with: + database_id: "$this.dashboard.database_id" + - type: "call" + name: "list-github-users" + call: "github.list-org-members" + with: + org: "naftiko" + - type: "lookup" + name: "match-contributors" + index: "list-github-users" + match: "login" + lookupValue: "$.query-tasks.assignee" + outputParameters: + - "login" + - "avatar_url" + - "html_url" + mappings: + - targetName: "contributors" + value: "$.match-contributors" + outputParameters: + - name: "contributors" + type: "array" + items: + type: "object" + properties: + login: + type: "string" + avatar_url: + type: "string" + html_url: + type: "string" + + consumes: + - type: "http" + namespace: "notion" + description: "Notion public API for database and page operations" + baseUri: "https://api.notion.com/v1" + authentication: + type: "bearer" + token: "{{notion_token}}" + inputParameters: + - name: "Notion-Version" + in: "header" + value: "2022-06-28" + resources: + - name: "db-query" + path: "/databases/{database_id}/query" + label: "Database Query" + operations: + - name: "query-database" + label: "Query Database" + method: "POST" + inputParameters: + - name: "database_id" + in: "path" + outputParameters: + - name: "assignee" + type: "string" + value: "$.results[*].properties.Assignee.people[0].name" + - name: "taskName" + type: "string" + value: "$.results[*].properties.Name.title[0].text.content" + + - type: "http" + namespace: "github" + description: "GitHub REST API for repository and user operations" + baseUri: "https://api.github.com" + authentication: + type: "bearer" + token: "{{github_token}}" + resources: + - name: "repos" + path: "/repos/{owner}/{repo}" + label: "Repositories" + operations: + - name: "get-repo" + label: "Get Repository" + method: "GET" + inputParameters: + - name: "owner" + in: "path" + - name: "repo" + in: "path" + outputParameters: + - name: "full_name" + type: "string" + value: "$.full_name" + - name: "stargazers_count" + type: "number" + value: "$.stargazers_count" + - name: "language" + type: "string" + value: "$.language" + - name: "org-members" + path: "/orgs/{org}/members" + label: "Organization Members" + operations: + - name: "list-org-members" + label: "List Organization Members" + method: "GET" + inputParameters: + - name: "org" + in: "path" + outputParameters: + - name: "login" + type: "string" + value: "$[*].login" + - name: "avatar_url" + type: "string" + value: "$[*].avatar_url" + - name: "html_url" + type: "string" + value: "$[*].html_url" +``` + +--- + +## 5. Versioning + +The Naftiko Specification uses semantic versioning. The `naftiko` field in the Naftiko Object specifies the exact version of the specification (e.g., `"0.4"`). + +Tools processing Naftiko documents MUST validate this field to ensure compatibility with the specification version they support. + +--- + +This specification defines how to describe modular, composable capabilities that consume multiple sources and expose unified interfaces, supporting orchestration, authentication, and flexible routing patterns. \ No newline at end of file diff --git a/src/main/resources/wiki/Specification.md b/src/main/resources/wiki/Specification.md new file mode 100644 index 0000000..c490040 --- /dev/null +++ b/src/main/resources/wiki/Specification.md @@ -0,0 +1,2112 @@ +# Naftiko Specification + +**Version 0.4** + +**Publication Date:** February 2026 + +--- + +- **Table of Contents** + +## 1. Introduction + +The Naftiko Specification defines a standard, language-agnostic interface for describing modular, composable capabilities. In short, a **capability** is a functional unit that consumes external APIs (sources) and exposes adapters that allow other systems to interact with it. + +A Naftiko capability focuses on declaring the **integration intent** — what a system needs to consume and what it exposes — rather than implementation details. This higher-level abstraction makes capabilities naturally suitable for AI-driven discovery, orchestration and integration use cases, and beyond. When properly defined, a capability can be discovered, orchestrated, validated and executed with minimal implementation logic. The specification enables description of: + +- **Consumed sources**: External APIs or services that the capability uses +- **Exposed adapters**: Server interfaces that the capability provides (HTTP, REST, etc.) +- **Orchestration**: How calls to consumed sources are combined and mapped to realize exposed functions +- **External references**: Variables and resources resolved from external sources + +### 1.1 Schema Access + +The JSON Schema for the Naftiko Specification is available in two forms: + +- **Raw file** — The schema source file is hosted on GitHub: [capability-schema.json](https://github.com/naftiko/framework/blob/main/src/main/resources/schemas/capability-schema.json) +- **Interactive viewer** — A human-friendly viewer is available at: [Schema Viewer](https://naftiko.github.io/schema-viewer/) + +### 1.2 Core Objects + +**Capability**: The central object that defines a modular functional unit with clear input/output contracts. + +**Consumes**: External sources (APIs, services) that the capability uses to realize its operations. + +**Exposes**: Server adapters that provide access to the capability's operations. + +**Resources**: API endpoints that group related operations. + +**Operations**: Individual HTTP operations (GET, POST, etc.) that can be performed on resources. + +**Namespace**: A unique identifier for consumed sources, used for routing and mapping with the expose layer. + +**ExternalRef**: A declaration of an external reference providing variables to the capability. Two variants: file-resolved (for development) and runtime-resolved (for production). Variables are explicitly declared via a `keys` map. + +### 1.3 Related Specifications. + + + +Three specifications that work better together. + +| | **OpenAPI** | **Arazzo** | **OpenCollections** | **Naftiko** | +| --- | --- | --- | --- | --- | +| **Focus** | Defines *what* your API is — the contract, the schema, the structure. | Defines *how* API calls are sequenced — the workflows between endpoints. | Defines *how* to use your API — the scenarios, the runnable collections. | Defines *what* a capability consumes and exposes — the integration intent. | +| **Scope** | Single API surface | Workflows across one or more APIs | Runnable collections of API calls | Modular capability spanning multiple APIs | +| **Key strengths** | ✓ Endpoints & HTTP methods
✓ Request/response schemas
✓ Authentication requirements
✓ Data types & validation
✓ SDK & docs generation | ✓ Multi-step sequences
✓ Step dependencies & data flow
✓ Success/failure criteria
✓ Reusable workflow definitions | ✓ Runnable, shareable collections
✓ Pre-request scripts & tests
✓ Environment variables
✓ Living, executable docs | ✓ Consume/expose duality
✓ Namespace-based routing
✓ Orchestration & forwarding
✓ AI-driven discovery
✓ Composable capabilities | +| **Analogy** | The *parts list* and dimensions | The *assembly sequence* between parts | The *step-by-step assembly guide* you can run | The *product blueprint* — what goes in, what comes out | +| **Best used when you need to…** | Define & document an API contract, generate SDKs, validate payloads | Describe multi-step API workflows with dependencies | Share runnable API examples, test workflows, onboard developers | Declare a composable capability that consumes sources and exposes unified interfaces | + +**OpenAPI** tells you the shape of the door. **Arazzo** describes the sequence of doors to walk through. **OpenCollections** lets you actually walk through them. **Naftiko** combines the features of those 3 specs into a single, coherent spec, reducing complexity and offering consistent tooling out of the box + +--- + +## 2. Format + +Naftiko specifications can be represented in YAML format, complying with the provided Naftiko schema which is made available in both JSON Schema and [JSON Structure](https://json-structure.org/) formats. + +All field names in the specification are **case-sensitive**. + +Naftiko Objects expose two types of fields: + +- **Fixed fields**: which have a declared name +- **Patterned fields**: which have a declared pattern for the field name + +--- + +## 3. Objects and Fields + +### 3.1 Naftiko Object + +This is the root object of the Naftiko document. + +#### 3.1.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **naftiko** | `string` | **REQUIRED**. Version of the Naftiko schema. MUST be `"0.4"` for this version. | +| **info** | `Info` | **REQUIRED**. Metadata about the capability. | +| **capability** | `Capability` | **REQUIRED**. Technical configuration of the capability including sources and adapters. | +| **externalRefs** | `ExternalRef[]` | List of external references for variable injection. Each entry declares injected variables via a `keys` map. | + +#### 3.1.2 Rules + +- The `naftiko` field MUST be present and MUST have the value `"0.4"` for documents conforming to this version of the specification. +- Both `info` and `capability` objects MUST be present. +- The `externalRefs` field is OPTIONAL. When present, it MUST contain at least one entry. +- No additional properties are allowed at the root level. + +--- + +### 3.2 Info Object + +Provides metadata about the capability. + +#### 3.2.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **label** | `string` | **REQUIRED**. The display name of the capability. | +| **description** | `string` | **REQUIRED**. A description of the capability. The more meaningful it is, the easier for agent discovery. | +| **tags** | `string[]` | List of tags to help categorize the capability for discovery and filtering. | +| **created** | `string` | Date the capability was created (format: `YYYY-MM-DD`). | +| **modified** | `string` | Date the capability was last modified (format: `YYYY-MM-DD`). | +| **stakeholders** | `Person[]` | List of stakeholders related to this capability (for discovery and filtering). | + +#### 3.2.2 Rules + +- Both `label` and `description` are mandatory. +- No additional properties are allowed. + +#### 3.2.3 Info Object Example + +```yaml +info: + label: Notion Page Creator + description: Creates and manages Notion pages with rich content formatting + tags: + - notion + - automation + created: "2026-02-17" + modified: "2026-02-17" + stakeholders: + - role: owner + fullName: "Jane Doe" + email: "jane.doe@example. +``` + +--- + +### 3.3 Person Object + +Describes a person related to the capability. + +#### 3.3.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **role** | `string` | **REQUIRED**. The role of the person in relation to the capability. E.g. owner, editor, viewer. | +| **fullName** | `string` | **REQUIRED**. The full name of the person. | +| **email** | `string` | The email address of the person. MUST match pattern `^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$`. | + +#### 3.3.2 Rules + +- Both `role` and `fullName` are mandatory. +- No additional properties are allowed. + +#### 3.3.3 Person Object Example + +```yaml +- role: owner + fullName: "Jane Doe" + email: "jane.doe@example.com" +``` + +--- + +### 3.4 Capability Object + +Defines the technical configuration of the capability. + +#### 3.4.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **exposes** | `Exposes[]` | **REQUIRED**. List of exposed server adapters. | +| **consumes** | `Consumes[]` | **REQUIRED**. List of consumed client adapters. | + +#### 3.4.2 Rules + +- The `exposes` array MUST contain at least one entry. +- The `consumes` array MUST contain at least one entry. +- Each `consumes` entry MUST include both `baseUri` and `namespace` fields. +- There are several types of exposed adapters and consumed sources objects, all will be described in following objects. +- No additional properties are allowed. + +#### 3.4.3 Namespace Uniqueness Rule + +When multiple `consumes` entries are present: + +- Each `namespace` value MUST be unique across all consumes entries. +- The `namespace` field is used for routing from the expose layer to the correct consumed source. +- Duplicate namespace values will result in ambiguous routing and are forbidden. + +#### 3.4.4 Capability Object Example + +```yaml +capability: + exposes: + - type: api + port: 3000 + namespace: tasks-api + resources: + - path: /tasks + description: "Endpoint to create tasks via the external API" + operations: + - method: POST + label: Create Task + call: api.create-task + outputParameters: + - type: string + mapping: $.taskId + consumes: + - type: http + namespace: api + baseUri: https://api.example.com + resources: + - name: tasks + label: Tasks API + path: /tasks + operations: + - name: create-task + label: Create Task + method: POST + inputParameters: + - name: task_id + in: path + outputParameters: + - name: taskId + type: string + value: $.data.id +``` + +--- + +### 3.5 Exposes Object + +Describes a server adapter that exposes functionality. + +> Update (schema v0.4): the exposition adapter is **API** with `type: "api"` (and a required `namespace`). Legacy `httpProxy` / `rest` exposition types are not part of the JSON Schema anymore. +> + +#### 3.5.1 API Expose + +API exposition configuration. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"api"`. | +| **address** | `string` | Server address. Can be a hostname, IPv4, or IPv6 address. | +| **port** | `integer` | **REQUIRED**. Port number. MUST be between 1 and 65535. | +| **authentication** | `Authentication` | Authentication configuration. | +| **namespace** | `string` | **REQUIRED**. Unique identifier for this exposed API. | +| **resources** | `ExposedResource[]` | **REQUIRED**. List of exposed resources. | + +#### 3.5.2 ExposedResource Object + +An exposed resource with **operations** and/or **forward** configuration. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **path** | `string` | **REQUIRED**. Path of the resource (supports `param` placeholders). | +| **description** | `string` | **REQUIRED**. Used to provide *meaningful* information about the resource. In a world of agents, context is king. | +| **name** | `string` | Technical name for the resource (used for references, pattern `^[a-zA-Z0-9-]+$`). | +| **label** | `string` | Display name for the resource (likely used in UIs). | +| **inputParameters** | `ExposedInputParameter[]` | Input parameters attached to the resource. | +| **operations** | `ExposedOperation[]` | Operations available on this resource. | +| **forward** | `ForwardConfig` | Forwarding configuration to a consumed namespace. | + +#### 3.5.3 Rules + +- Both `description` and `path` are mandatory. +- At least one of `operations` or `forward` MUST be present. Both can coexist on the same resource. +- if both `operations` or `forward` are present, in case of conflict, `operations` takes precendence on `forward`. +- No additional properties are allowed. + +#### 3.5.4 Address Validation Patterns + +- **Hostname**: `^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(\\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` +- **IPv4**: `^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$` +- **IPv6**: `^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$` + +#### 3.5.5 Exposes Object Examples + +**API Expose with operations:** + +```yaml +type: api +port: 3000 +namespace: sample +resources: + - path: /status + description: "Health check endpoint" + name: health + operations: + - name: get-status + method: GET + call: api.health-check + outputParameters: + - type: string + mapping: $.status +``` + +**API Expose with forward:** + +```yaml +type: api +port: 8080 +namespace: proxy +resources: + - path: /notion/{path} + description: "Forward requests to the Notion API" + forward: + targetNamespace: notion + trustedHeaders: + - Notion-Version +``` + +**API Expose with both operations and forward:** + +```yaml +type: api +port: 9090 +namespace: hybrid +resources: + - path: /data/{path} + description: "Resource with orchestrated operations and pass-through forwarding" + operations: + - name: get-summary + method: GET + call: api.get-summary + forward: + targetNamespace: api + trustedHeaders: + - Authorization +``` + +--- + +### 3.6 Consumes Object + +Describes a client adapter for consuming external APIs. + +> Update (schema v0.4): `targetUri` is now `baseUri`. The `headers` field has been removed — use `inputParameters` with `in: "header"` instead. +> + +#### 3.6.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. Type of consumer. Valid values: `"http"`. | +| **namespace** | `string` | Path suffix used for routing from exposes. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **baseUri** | `string` | **REQUIRED**. Base URI for the consumed API. Must be a valid http(s) URL (no `path` placeholder in the schema). | +| **authentication** | Authentication Object | Authentication configuration. Defaults to `"inherit"`. | +| **description** | `string` | **REQUIRED**. A description of the consumed API. The more meaningful it is, the easier for agent discovery. | +| **inputParameters** | `ConsumedInputParameter[]` | Input parameters applied to all operations in this consumed API. | +| **resources** | [ConsumedHttpResource Object] | **REQUIRED**. List of API resources. | + +#### 3.6.2 Rules + +- The `type` field MUST be `"http"`. +- The `baseUri` field is required. +- The `namespace` field is required and MUST be unique across all consumes entries. +- The `namespace` value MUST match the pattern `^[a-zA-Z0-9-]+$` (alphanumeric and hyphens only). +- The `description` field is required. +- The `resources` array is required and MUST contain at least one entry. + +#### 3.6.3 Base URI Format + +The `baseUri` field MUST be a valid `http://` or `https://` URL, and may optionally include a base path. + +Example: `https://api.github.com` or `https://api.github.com/v3` + +#### 3.6.4 Consumes Object Example + +```yaml +type: http +namespace: github +baseUri: https://api.github.com +authentication: + type: bearer + token: "{{github_token}}" +inputParameters: + - name: Accept + in: header + value: "application/vnd.github.v3+json" +resources: + - name: users + label: Users API + path: /users/{username} + operations: + - name: get-user + label: Get User + method: GET + inputParameters: + - name: username + in: path + outputParameters: + - name: userId + type: string + value: $.id + - name: repos + label: Repositories API + path: /users/{username}/repos + operations: + - name: list-repos + label: List Repositories + method: GET + inputParameters: + - name: username + in: path + outputParameters: + - name: repos + type: array + value: $ +``` + +--- + +### 3.7 ConsumedHttpResource Object + +Describes an API resource that can be consumed from an external API. + +#### 3.7.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Technical name for the resource. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **label** | `string` | Display name of the resource. | +| **description** | `string` | Description of the resource. | +| **path** | `string` | **REQUIRED**. Path of the resource, relative to the consumes `baseUri`. | +| **inputParameters** | `ConsumedInputParameter[]` | Input parameters for this resource. | +| **operations** | `ConsumedHttpOperation[]` | **REQUIRED**. List of operations for this resource. | + +#### 3.7.2 Rules + +- The `name` field MUST be unique within the parent consumes object's resources array. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-]+$` (alphanumeric and hyphens only). +- The `path` field will be appended to the parent consumes object's `baseUri`. +- The `operations` array MUST contain at least one entry. +- No additional properties are allowed. + +#### 3.7.3 ConsumedHttpResource Object Example + +```yaml +name: users +label: Users API +path: /users/{username} +inputParameters: + - name: username + in: path +operations: + - name: get-user + label: Get User + method: GET + outputParameters: + - name: userId + type: string + value: $.id +``` + +--- + +### 3.8 ConsumedHttpOperation Object + +Describes an operation that can be performed on a consumed resource. + +#### 3.8.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Technical name for the operation. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **label** | `string` | Display name of the operation. | +| **description** | `string` | A longer description of the operation for documentation purposes. | +| **method** | `string` | **REQUIRED**. HTTP method. One of: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. Default: `GET`. | +| **inputParameters** | `ConsumedInputParameter[]` | Input parameters for the operation. | +| **outputRawFormat** | `string` | The raw format of the response. One of: `json`, `xml`, `avro`, `protobuf`, `csv`, `yaml` . Default: `json`. | +| **outputParameters** | `ConsumedOutputParameter[]` | Output parameters extracted from the response via JsonPath. | +| **body** | `RequestBody` | Request body configuration. | + +#### 3.8.2 Rules + +- The `name` field MUST be unique within the parent resource's operations array. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-]+$` (alphanumeric and hyphens only). +- Both `name` and `method` are mandatory. +- No additional properties are allowed. + +#### 3.8.3 ConsumedHttpOperation Object Example + +```yaml +name: get-user +label: Get User Profile +method: GET +inputParameters: + - name: username + in: path +outputParameters: + - name: userId + type: string + value: $.id + - name: username + type: string + value: $.login + - name: email + type: string + value: $.email +``` + +--- + +### 3.9 ExposedOperation Object + +Describes an operation exposed on an exposed resource. + +> Update (schema v0.4): ExposedOperation now supports two modes via `oneOf` — **simple** (direct call with mapped output) and **orchestrated** (multi-step with named operation). The `call` and `with` fields are new. The `name` and `steps` fields are only required in orchestrated mode. +> + +#### 3.9.1 Fixed Fields + +All fields available on ExposedOperation: + +| Field Name | Type | Description | +| --- | --- | --- | +| **method** | `string` | **REQUIRED**. HTTP method. One of: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. | +| **name** | `string` | Technical name for the operation (pattern `^[a-zA-Z0-9-]+$`). **REQUIRED in orchestrated mode only.** | +| **label** | `string` | Display name for the operation (likely used in UIs). | +| **description** | `string` | A longer description of the operation. Useful for agent discovery and documentation. | +| **inputParameters** | `ExposedInputParameter[]` | Input parameters attached to the operation. | +| **call** | `string` | **Simple mode only**. Direct reference to a consumed operation. Format: `{namespace}.{operationId}`. MUST match pattern `^[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+$`. | +| **with** | `WithInjector` | **Simple mode only**. Parameter injection for the called operation. | +| **outputParameters** (simple) | `MappedOutputParameter[]` | **Simple mode**. Output parameters mapped from the consumed operation response. | +| **steps** | `OperationStep[]` | **Orchestrated mode only. REQUIRED** (at least 1 step). Sequence of calls to consumed operations. | +| **outputParameters** (orchestrated) | `OrchestratedOutputParameter[]` | **Orchestrated mode**. Output parameters with name and type. | +| **mappings** | `StepOutputMapping[]` | **Orchestrated mode only**. Maps step outputs to the operation's output parameters at the operation level. | + +#### 3.9.2 Modes (oneOf) + +**Simple mode** — A direct call to a single consumed operation: + +- `call` is **REQUIRED** +- `with` is optional (inject parameters into the call) +- `outputParameters` are `MappedOutputParameter[]` (type + mapping) +- `steps` MUST NOT be present +- `name` is optional + +**Orchestrated mode** — A multi-step orchestration: + +- `name` is **REQUIRED** +- `steps` is **REQUIRED** (at least 1 entry) +- `mappings` is optional — maps step outputs to the operation's output parameters at the operation level +- `outputParameters` are `OrchestratedOutputParameter[]` (name + type) +- `call` and `with` MUST NOT be present + +#### 3.9.3 Rules + +- Exactly one of the two modes MUST be used (simple or orchestrated). +- In simple mode, `call` MUST follow the format `{namespace}.{operationId}` and reference a valid consumed operation. +- In orchestrated mode, the `steps` array MUST contain at least one entry. Each step references a consumed operation using `{namespace}.{operationName}`. +- The `method` field is always required regardless of mode. + +#### 3.9.4 ExposedOperation Object Examples + +**Simple mode (direct call):** + +```yaml +method: GET +label: Get User Profile +call: github.get-user +with: + username: $this.sample.username +outputParameters: + - type: string + mapping: $.login + - type: number + mapping: $.id +``` + +**Orchestrated mode (multi-step):** + +```yaml +name: get-db +method: GET +label: Get Database +inputParameters: + - name: database_id + in: path + type: string + description: The ID of the database to retrieve +steps: + - type: call + name: fetch-db + call: notion.get-database + with: + database_id: "$this.sample.database_id" +mappings: + - targetName: db_name + value: "$.dbName" +outputParameters: + - name: db_name + type: string + - name: Api-Version + type: string +``` + +--- + +### 3.10 RequestBody Object + +Describes request body configuration for consumed operations. `RequestBody` is a `oneOf` — exactly one of five subtypes must be used. + +#### 3.10.1 Subtypes + +**RequestBodyJson** — JSON body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"json"`. | +| **data** | `string` | `object` | `array` | **REQUIRED**. The JSON payload. Can be a raw JSON string, an inline object, or an array. | + +**RequestBodyText** — Plain text, XML, or SPARQL body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. One of: `"text"`, `"xml"`, `"sparql"`. | +| **data** | `string` | **REQUIRED**. The text payload. | + +**RequestBodyFormUrlEncoded** — URL-encoded form body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"formUrlEncoded"`. | +| **data** | `string` | `object` | **REQUIRED**. Either a raw URL-encoded string or an object whose values are strings. | + +**RequestBodyMultipartForm** — Multipart form body + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"multipartForm"`. | +| **data** | `RequestBodyMultipartFormPart[]` | **REQUIRED**. Array of form parts. Each part has: `name` (required), `value` (required), `filename` (optional), `contentType` (optional). | + +**RequestBodyRaw** — Raw string body + +`RequestBody` can also be a plain `string`. When used with a YAML block scalar (`|`), the string is sent as-is. Interpreted as JSON by default. + +#### 3.10.2 Rules + +- Exactly one of the five subtypes must be used. +- For structured subtypes, both `type` and `data` are mandatory. +- No additional properties are allowed on any subtype. + +#### 3.10.3 RequestBody Examples + +**JSON body (object):** + +```yaml +body: + type: json + data: + hello: "world" +``` + +**JSON body (string):** + +```yaml +body: + type: json + data: '{"key": "value"}' +``` + +**Text body:** + +```yaml +body: + type: text + data: "Hello, world!" +``` + +**Form URL-encoded body:** + +```yaml +body: + type: formUrlEncoded + data: + username: "admin" + password: "secret" +``` + +**Multipart form body:** + +```yaml +body: + type: multipartForm + data: + - name: "file" + value: "base64content..." + filename: "document.pdf" + contentType: "application/pdf" + - name: "description" + value: "My uploaded file" +``` + +**Raw body:** + +```yaml +body: | + { + "filter": { + "property": "Status", + "select": { "equals": "Active" } + } + } +``` + +--- + +### 3.11 InputParameter Objects + +> Update (schema v0.4): The single `InputParameter` object has been split into two distinct types: **ConsumedInputParameter** (used in consumes) and **ExposedInputParameter** (used in exposes, with additional `type` and `description` fields required). +> + +#### 3.11.1 ConsumedInputParameter Object + +Used in consumed resources and operations. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Parameter name. MUST match pattern `^[a-zA-Z0-9-*]+$`. | +| **in** | `string` | **REQUIRED**. Parameter location. Valid values: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. | +| **value** | `string` | Value or JSONPath reference. | + +**Rules:** + +- Both `name` and `in` are mandatory. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-*]+$`. +- The `in` field MUST be one of: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. +- A unique parameter is defined by the combination of `name` and `in`. +- No additional properties are allowed. + +**ConsumedInputParameter Example:** + +```yaml +- name: username + in: path +- name: page + in: query +- name: Authorization + in: header + value: "Bearer token" +``` + +#### 3.11.2 ExposedInputParameter Object + +Used in exposed resources and operations. Extends the consumed variant with `type` and `description` (both required) for agent discoverability, plus an optional `pattern` for validation. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Parameter name. MUST match pattern `^[a-zA-Z0-9-*]+$`. | +| **in** | `string` | **REQUIRED**. Parameter location. Valid values: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. | +| **type** | `string` | **REQUIRED**. Data type of the parameter. One of: `string`, `number`, `boolean`, `object`, `array`. | +| **description** | `string` | **REQUIRED**. Human-readable description of the parameter. Essential for agent discovery. | +| **pattern** | `string` | Optional regex pattern for parameter value validation. | +| **value** | `string` | Default value or JSONPath reference. | + +**Rules:** + +- All of `name`, `in`, `type`, and `description` are mandatory. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-*]+$`. +- The `in` field MUST be one of: `"query"`, `"header"`, `"path"`, `"cookie"`, `"body"`. +- The `type` field MUST be one of: `"string"`, `"number"`, `"boolean"`, `"object"`, `"array"`. +- No additional properties are allowed. + +**ExposedInputParameter Example:** + +```yaml +- name: database_id + in: path + type: string + description: The unique identifier of the Notion database + pattern: "^[a-f0-9-]+$" +- name: page_size + in: query + type: number + description: Number of results per page (max 100) +``` + +--- + +### 3.12 OutputParameter Objects + +> Update (schema v0.4): The single `OutputParameter` object has been split into three distinct types: **ConsumedOutputParameter** (used in consumed operations), **MappedOutputParameter** (used in simple-mode exposed operations), and **OrchestratedOutputParameter** (used in orchestrated-mode exposed operations). +> + +#### 3.12.1 ConsumedOutputParameter Object + +Used in consumed operations to extract values from the raw API response. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Output parameter name. MUST match pattern `^[a-zA-Z0-9-_]+$`. | +| **type** | `string` | **REQUIRED**. Data type. One of: `string`, `number`, `boolean`, `object`, `array`. | +| **value** | `string` | **REQUIRED**. JsonPath expression to extract value from consumed function response. | + +**Rules:** + +- All three fields (`name`, `type`, `value`) are mandatory. +- The `name` field MUST match the pattern `^[a-zA-Z0-9-_*]+$`. +- The `value` field MUST start with `$`. +- No additional properties are allowed. + +**ConsumedOutputParameter Example:** + +```yaml +outputParameters: + - name: dbName + type: string + value: $.title[0].text.content + - name: dbId + type: string + value: $.id +``` + +#### 3.12.2 MappedOutputParameter Object + +Used in **simple mode** exposed operations. Maps a value from the consumed response using `type` and `mapping`. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. Data type. One of: `string`, `number`, `boolean`, `object`, `array`. | +| **mapping** | `string` | `object` | **REQUIRED**. For scalar types (`string`, `number`, `boolean`): a JsonPath string. For `object`: an object with `properties` (recursive MappedOutputParameter map). For `array`: an object with `items` (recursive MappedOutputParameter). | + +**Subtypes by type:** + +- **`string`**, **`number`**, **`boolean`**: `mapping` is a JsonPath string (e.g. `$.login`) +- **`object`**: `mapping` is `{ properties: { key: MappedOutputParameter, ... } }` — recursive +- **`array`**: `mapping` is `{ items: MappedOutputParameter }` — recursive + +**Rules:** + +- Both `type` and `mapping` are mandatory. +- No additional properties are allowed. + +**MappedOutputParameter Examples:** + +```yaml +# Scalar mapping +outputParameters: + - type: string + mapping: $.login + - type: number + mapping: $.id + +# Object mapping (recursive) +outputParameters: + - type: object + mapping: + properties: + username: + type: string + mapping: $.login + userId: + type: number + mapping: $.id + +# Array mapping (recursive) +outputParameters: + - type: array + mapping: + items: + type: object + mapping: + properties: + name: + type: string + mapping: $.name +``` + +#### 3.12.3 OrchestratedOutputParameter Object + +Used in **orchestrated mode** exposed operations. Declares an output by `name` and `type` (the value is populated via step mappings). + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Output parameter name. | +| **type** | `string` | **REQUIRED**. Data type. One of: `string`, `number`, `boolean`, `object`, `array`. | + +**Subtypes by type:** + +- **`string`**, **`number`**, **`boolean`** (scalar): only `name` and `type` +- **`array`**: adds `items` (recursive OrchestratedOutputParameter without `name`) +- **`object`**: adds `properties` (map of name → recursive OrchestratedOutputParameter without `name`) + +**Rules:** + +- Both `name` and `type` are mandatory. +- No additional properties are allowed. + +**OrchestratedOutputParameter Example:** + +```yaml +outputParameters: + - name: db_name + type: string + - name: Api-Version + type: string + - name: results + type: array + items: + type: object + properties: + id: + type: string + title: + type: string +``` + +#### 3.12.4 JsonPath roots (extensions) + +In a consumed resource, **`$`** refers to the *raw response payload* of the consumed operation (after decoding based on `outputRawFormat`). The root `$` gives direct access to the JSON response body. + +Example, if you consider the following JSON response : + +```json +{ + "id": "154548", + "titles": [ + { + "text": { + "content": "This is title[0].text.content", + "author": "user1" + } + } + ], + "created_time": "2024-06-01T12:00:00Z" +} +``` + +- `$.id` is `154548` +- `$.titles[0].text.content` is `This is title[0].text.content` + +#### 3.12.5 Common patterns + +- `$.fieldName` — accesses a top-level field +- `$.data.user.id` — accesses nested fields +- `$.items[0]` — accesses array elements +- `$.items[*].id` — accesses all ids in an array + +--- + +### 3.13 OperationStep Object + +Describes a single step in an orchestrated operation. `OperationStep` is a `oneOf` between two subtypes: **OperationStepCall** and **OperationStepLookup**, both sharing a common **OperationStepBase**. + +> Update (schema v0.4): OperationStep is now a discriminated union (`oneOf`) with a required `type` field (`"call"` or `"lookup"`) and a required `name` field. `OperationStepCall` uses `with` (WithInjector) instead of `inputParameters`. `OperationStepLookup` is entirely new. +> + +#### 3.13.1 OperationStepBase (shared fields) + +All operation steps share these base fields: + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. Step type discriminator. One of: `"call"`, `"lookup"`. | +| **name** | `string` | **REQUIRED**. Technical name for the step (pattern `^[a-zA-Z0-9-]+$`). Used as namespace for referencing step outputs in mappings and expressions. | + +#### 3.13.2 OperationStepCall + +Calls a consumed operation. + +**Fixed Fields** (in addition to base): + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"call"`. | +| **name** | `string` | **REQUIRED**. Step name (from base). | +| **call** | `string` | **REQUIRED**. Reference to consumed operation. Format: `{namespace}.{operationId}`. MUST match pattern `^[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+$`. | +| **with** | `WithInjector` | Parameter injection for the called operation. Keys are parameter names, values are strings or numbers (static values or `$this` references). | + +**Rules:** + +- `type`, `name`, and `call` are mandatory. +- The `call` field MUST follow the format `{namespace}.{operationName}`. +- The `namespace` portion MUST correspond to a namespace defined in one of the capability's consumes entries. +- The `operationName` portion MUST correspond to an operation `name` defined in the consumes entry identified by the namespace. +- `with` uses the same `WithInjector` object as simple-mode ExposedOperation (see §3.18). +- No additional properties are allowed. + +#### 3.13.3 OperationStepLookup + +Performs a lookup against the output of a previous call step, matching values and extracting fields. + +**Fixed Fields** (in addition to base): + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"lookup"`. | +| **name** | `string` | **REQUIRED**. Step name (from base). | +| **index** | `string` | **REQUIRED**. Name of a previous call step whose output serves as the lookup table. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **match** | `string` | **REQUIRED**. Name of the key field in the index to match against. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **lookupValue** | `string` | **REQUIRED**. JsonPath expression resolving to the value(s) to look up. | +| **outputParameters** | `string[]` | **REQUIRED**. List of field names to extract from the matched index entries (minimum 1 entry). | + +**Rules:** + +- `type`, `name`, `index`, `match`, `lookupValue`, and `outputParameters` are all mandatory. +- `outputParameters` MUST contain at least one entry. +- The `index` value MUST reference the `name` of a previous `call` step in the same orchestration. +- No additional properties are allowed. + +#### 3.13.4 Call Reference Resolution + +The `call` value on an `OperationStepCall` is resolved as follows: + +1. Split the value on the `.` character into namespace and operationName +2. Find the consumes entry with matching `namespace` field +3. Within that consumes entry's resources, find the operation with matching `name` field +4. Execute that operation as part of the orchestration sequence + +#### 3.13.5 OperationStep Object Examples + +**Call step with parameter injection:** + +```yaml +steps: + - type: call + name: fetch-db + call: notion.get-database + with: + database_id: $this.sample.database_id +``` + +**Lookup step (match against a previous call's output):** + +```yaml +steps: + - type: call + name: list-users + call: github.list-users + - type: lookup + name: find-user + index: list-users + match: email + lookupValue: $this.sample.user_email + outputParameters: + - login + - id +``` + +**Multi-step orchestration (call + lookup):** + +```yaml +steps: + - type: call + name: get-entries + call: api.list-entries + - type: lookup + name: resolve-entry + index: get-entries + match: entry_id + lookupValue: $this.sample.target_id + outputParameters: + - title + - status + - type: call + name: post-result + call: slack.post-message + with: + text: $this.sample.title +``` + +--- + +### 3.14 StepOutputMapping Object + +Describes how to map the output of an operation step to the input of another step or to the output of the exposed operation. + +#### 3.14.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **targetName** | `string` | **REQUIRED**. The name of the parameter to map to. It can be an input parameter of a next step or an output parameter of the exposed operation. | +| **value** | `string` | **REQUIRED**. A JsonPath reference to the value to map from. E.g. `$.get-database.database_id`. | + +#### 3.14.2 Rules + +- Both `targetName` and `value` are mandatory. +- No additional properties are allowed. + +#### 3.14.3 How mappings wire steps to exposed outputs + +A StepOutputMapping connects the **output parameters of a consumed operation** (called by the step) to the **output parameters of the exposed operation** (or to input parameters of subsequent steps). + +- **`targetName`** — refers to the `name` of an output parameter declared on the exposed operation, or the `name` of an input parameter of a subsequent step. The target parameter receives its value from the mapping. +- **`value`** — a JsonPath expression where **`$`** is the root of the consumed operation's output parameters. The syntax `$.{outputParameterName}` references a named output parameter of the consumed operation called in this step. + +#### 3.14.4 End-to-end example + +Consider a consumed operation `notion.get-database` that declares: + +```yaml +# In consumes → resources → operations +name: "get-database" +outputParameters: + - name: "dbName" + value: "$.title[0].text.content" +``` + +And the exposed side of the capability: + +```yaml +# In exposes +exposes: + - type: "api" + address: "localhost" + port: 9090 + namespace: "sample" + resources: + - path: "/databases/{database_id}" + name: "db" + label: "Database resource" + description: "Retrieve information about a Notion database" + inputParameters: + - name: "database_id" + in: "path" + type: "string" + description: "The unique identifier of the Notion database" + operations: + - name: "get-db" + method: "GET" + label: "Get Database" + outputParameters: + - name: "db_name" + type: "string" + steps: + - type: "call" + name: "fetch-db" + call: "notion.get-database" + with: + database_id: "$this.sample.database_id" + mappings: + - targetName: "db_name" + value: "$.dbName" +``` + +Here is what happens at orchestration time: + +1. The step `fetch-db` calls `notion.get-database`, which extracts `dbName` and `dbId` from the raw response via its own output parameters. +2. The `with` injector passes `database_id` from the exposed input parameter (`$this.sample.database_id`) to the consumed operation. +3. The mapping `targetName: "db_name"` refers to the exposed operation's output parameter `db_name`. +4. The mapping `value: "$.dbName"` resolves to the value of the consumed operation's output parameter named `dbName`. +5. As a result, the exposed output `db_name` is populated with the value extracted by `$.dbName` (i.e. `title[0].text.content` from the raw Notion API response). + +#### 3.14.5 StepOutputMapping Object Example + +```yaml +mappings: + - targetName: "db_name" + value: "$.dbName" +``` + +--- + +### 3.15 `$this` Context Reference + +Describes how `$this` references work in `with` (WithInjector) and other expression contexts. + +> Update (schema v0.4): The former `OperationStepParameter` object (with `name` and `value` fields) has been replaced by `WithInjector` (see §3.18). This section now documents the `$this` expression root, which is used within `WithInjector` values. +> + +#### 3.15.1 The `$this` root + +In a `with` (WithInjector) value — whether on an ExposedOperation (simple mode) or an OperationStepCall — the **`$this`** root references the *current capability execution context*, i.e. values already resolved during orchestration. + +**`$this`** navigates the expose layer's input parameters using the path `$this.{exposeNamespace}.{inputParameterName}`. This allows a step or a simple-mode call to receive values that were provided by the caller of the exposed operation. + +- **`$this.{exposeNamespace}.{paramName}`** — accesses an input parameter of the exposed resource or operation identified by its namespace. +- The `{exposeNamespace}` corresponds to the `namespace` of the exposed API. +- The `{paramName}` corresponds to the `name` of an input parameter declared on the exposed resource or operation. + +#### 3.15.2 Example + +If the exposed API has namespace `sample` and an input parameter `database_id` declared on its resource, then: + +- `$this.sample.database_id` resolves to the value of `database_id` provided by the caller. + +**Usage in a WithInjector:** + +```yaml +call: notion.get-database +with: + database_id: $this.sample.database_id +``` + +--- + +### 3.16 Authentication Object + +Defines authentication configuration. Four types are supported: basic, apikey, bearer, and digest. + +#### 3.16.1 Basic Authentication + +HTTP Basic Authentication. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"basic"`. | +| **username** | `string` | Username for basic auth. | +| **password** | `string` | Password for basic auth. | + +**Example:** + +```yaml +authentication: + type: basic + username: admin + password: "secret_password" +``` + +#### 3.16.2 API Key Authentication + +API Key authentication via header or query parameter. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"apikey"`. | +| **key** | `string` | API key name (header name or query parameter name). | +| **value** | `string` | API key value. | +| **placement** | `string` | Where to place the key. Valid values: `"header"`, `"query"`. | + +**Example:** + +```yaml +authentication: + type: apikey + key: X-API-Key + value: "{{api_key}}" + placement: header +``` + +#### 3.16.3 Bearer Token Authentication + +Bearer token authentication. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"bearer"`. | +| **token** | `string` | Bearer token value. | + +**Example:** + +```yaml +authentication: + type: bearer + token: "bearer_token" +``` + +#### 3.16.4 Digest Authentication + +HTTP Digest Authentication. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **type** | `string` | **REQUIRED**. MUST be `"digest"`. | +| **username** | `string` | Username for digest auth. | +| **password** | `string` | Password for digest auth. | + +**Example:** + +```yaml +authentication: + type: digest + username: admin + password: "secret_password" +``` + +#### 3.16.5 Rules + +- Only one authentication type can be used per authentication object. +- The `type` field determines which additional fields are required or allowed. +- Authentication can be specified at multiple levels (exposes, consumes) with inner levels overriding outer levels. + +--- + +### 3.17 ForwardConfig Object + +Defines forwarding configuration for an exposed resource to pass requests through to a consumed namespace. + +> Update (schema v0.4): Renamed from `ForwardHeaders` to `ForwardConfig`. The `targetNamespaces` array has been replaced by a single `targetNamespace` string. +> + +#### 3.17.1 Fixed Fields + +| Field Name | Type | Description | +| --- | --- | --- | +| **targetNamespace** | `string` | **REQUIRED**. The consumer namespace to forward requests to. MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **trustedHeaders** | [`string`] | **REQUIRED**. List of headers allowed to be forwarded (minimum 1 entry). No wildcards supported. | + +#### 3.17.2 Rules + +- The `targetNamespace` field is mandatory and MUST reference a valid namespace from one of the capability's consumes entries. +- The `trustedHeaders` array is mandatory and MUST contain at least one entry. +- Header names in `trustedHeaders` are case-insensitive (following HTTP header conventions). +- Only headers listed in `trustedHeaders` will be forwarded to the consumed source. +- No additional properties are allowed. + +#### 3.17.3 ForwardConfig Object Example + +```yaml +forward: + targetNamespace: notion + trustedHeaders: + - Authorization + - Notion-Version +``` + +--- + +### 3.18 WithInjector Object + +Defines parameter injection for simple-mode exposed operations. Used with the `with` field on an ExposedOperation to inject values into the called consumed operation. + +> New in schema v0.4. +> + +#### 3.18.1 Shape + +`WithInjector` is an object whose keys are parameter names and whose values are static values or `$this` references. + +- Each key corresponds to a parameter `name` in the consumed operation's `inputParameters`. +- Each value is a `string` or a `number`: either a static value or a `$this.{namespace}.{paramName}` reference. + +#### 3.18.2 Rules + +- The keys MUST correspond to valid parameter names in the consumed operation being called. +- Values can be strings or numbers. +- String values can use the `$this` root to reference exposed input parameters (same as in OperationStepParameter). +- No additional constraints. + +#### 3.18.3 WithInjector Object Example + +```yaml +call: github.get-user +with: + username: $this.sample.username + Accept: "application/json" + maxRetries: 3 +``` + +--- + +### 3.19 ExternalRef Object + +> **Updated**: ExternalRef is now a discriminated union (`oneOf`) with two variants — **file-resolved** (for local development) and **runtime-resolved** (for production). Variables are explicitly declared via a `keys` map. +> + +Declares an external reference that provides variables to the capability. External references are declared at the root level of the Naftiko document via the `externalRefs` array. + +`ExternalRef` is a `oneOf` — exactly one of the two variants must be used. + +#### 3.19.1 File-Resolved ExternalRef + +Loads variables from a local file. Intended for **local development only**. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Unique identifier (kebab-case). MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **description** | `string` | **REQUIRED**. Used to provide *meaningful* information about the external reference. In a world of agents, context is king. | +| **type** | `string` | **REQUIRED**. MUST be `"environment"`. | +| **resolution** | `string` | **REQUIRED**. MUST be `"file"`. | +| **uri** | `string` | **REQUIRED**. URI pointing to the file (e.g. `file:///path/to/env.json`). | +| **keys** | `ExternalRefKeys` | **REQUIRED**. Map of variable names to keys in the resolved file content. | + +**Rules:** + +- All fields (`name`, `description`, `type`, `resolution`, `uri`, `keys`) are mandatory. +- No additional properties are allowed. + +#### 3.19.2 Runtime-Resolved ExternalRef + +Variables are injected by the execution environment at startup (default). The capability document does **not** specify where the values come from — this is delegated to the deployment platform. + +**Fixed Fields:** + +| Field Name | Type | Description | +| --- | --- | --- | +| **name** | `string` | **REQUIRED**. Unique identifier (kebab-case). MUST match pattern `^[a-zA-Z0-9-]+$`. | +| **type** | `string` | **REQUIRED**. MUST be `"environment"`. | +| **resolution** | `string` | **REQUIRED.** MUST be `"runtime"`. | +| **keys** | `ExternalRefKeys` | **REQUIRED**. Map of variable names to keys in the runtime context. | + +**Rules:** + +- `name`, `type`, and `keys` are mandatory. +- `resolution` is optional; when present MUST be `"runtime"`. +- No additional properties are allowed. + +Typical production providers include: + +- **HashiCorp Vault** — centralized secrets management +- **Kubernetes Secrets** / **ConfigMaps** — native K8s secret injection +- **AWS Secrets Manager** / **AWS SSM Parameter Store** +- **Azure Key Vault** +- **GCP Secret Manager** +- **Docker Secrets** — for containerized deployments +- **CI/CD pipeline variables** (GitHub Actions secrets, GitLab CI variables, etc.) + +#### 3.19.3 ExternalRefKeys Object + +A map of key-value pairs that define the variables to be injected from the external reference. + +- Each **key** is the variable name used for injection (available as `\{\{key\}\}` in the capability definition) +- Each **value** is the corresponding key in the resolved file content or runtime context + +Example: `{"notion_token": "NOTION_INTEGRATION_TOKEN"}` means the value of `NOTION_INTEGRATION_TOKEN` in the source will be injected as `{{notion_token}}` in the capability definition. + +**Schema:** + +```json +{ + "type": "object", + "additionalProperties": { "type": "string" } +} +``` + +#### 3.19.4 Rules + +- Each `name` value MUST be unique across all `externalRefs` entries. +- The `name` value MUST NOT collide with any `consumes` namespace to avoid ambiguity. +- The `keys` map MUST contain at least one entry. +- No additional properties are allowed on either variant. + + + +#### 3.19.5 ExternalRef Object Examples + +**File resolution (development):** + +```yaml +externalRefs: + - name: "notion-env" + type: "environment" + description: "External reference to Notion API for accessing project data stored in Notion." + resolution: file + uri: "file:///path/to/notion_env.json" + keys: + notion_token: "NOTION_INTEGRATION_TOKEN" + notion_projects_db_id: "PROJECTS_DATABASE_ID" + notion_time_tracker_db_id: "TIME_TRACKER_DATABASE_ID" +``` + +**Runtime resolution (production):** + +```yaml +externalRefs: + - name: "secrets" + type: "environment" + resolution: runtime + keys: + notion_token: "NOTION_INTEGRATION_TOKEN" + github_token: "GITHUB_TOKEN" +``` + +**Minimal runtime (resolution omitted — defaults to runtime):** + +```yaml +externalRefs: + - name: "env" + type: "environment" + keys: + api_key: "API_KEY" +``` + +--- + +### 3.20 Expression Syntax + +Variables declared in `externalRefs` via the `keys` map are injected into the capability document using mustache-style `\{\{variable\}\}` expressions. + +#### 3.20.1 Format + +The expression format is `\{\{key\}\}`, where `key` is a variable name declared in the `keys` map of an `externalRefs` entry. + +Expressions can appear in any `string` value within the document, including authentication tokens, header values, and input parameter values. + +#### 3.20.2 Resolution + +At runtime, expressions are resolved as follows: + +1. Find the `externalRefs` entry whose `keys` map contains the referenced variable name +2. Look up the corresponding source key in the `keys` map +3. Resolve the source key value using the strategy defined by `resolution` (`file` lookup or `runtime` injection) +4. Replace the `\{\{variable\}\}` expression with the resolved value + +If a referenced variable is not declared in any `externalRefs` entry's `keys`, the document MUST be considered invalid. + +#### 3.20.3 Relationship with `$this` + +`\{\{variable\}\}` expressions and `$this` references serve different purposes: + +- `\{\{variable\}\}` resolves **static configuration** from external references (secrets, environment variables) declared via `keys` +- `$this.{exposeNamespace}.{paramName}` resolves **runtime orchestration** values from the expose layer's input parameters + +The two expression systems are independent and MUST NOT be mixed. + +#### 3.20.4 Expression Examples + +```yaml +# Authentication token from external ref +authentication: + type: bearer + token: "{{notion_token}}" + +# Input parameter with header value from external ref +inputParameters: + - name: Authorization + in: header + value: "Bearer {{api_key}}" + +# Corresponding externalRefs declaration +externalRefs: + - name: "env" + type: "environment" + keys: + notion_token: "NOTION_TOKEN" + api_key: "API_KEY" +``` + +--- + +## 4. Complete Examples + +This section provides progressive examples — from the simplest capability to a full-featured one — to illustrate the main patterns of the specification. All examples are pseudo-functional and use realistic API shapes. + +### 4.1 Forward-only capability (proxy) + +The simplest capability: forward incoming requests to a consumed API without any transformation. + +```yaml +--- +naftiko: "0.4" +info: + label: "Notion Proxy" + description: "Pass-through proxy to the Notion API for development and debugging" + tags: + - proxy + - notion + created: "2026-02-01" + modified: "2026-02-01" + +capability: + exposes: + - type: "api" + port: 8080 + namespace: "proxy" + resources: + - path: "/notion/{path}" + description: "Forwards all requests to the Notion API" + forward: + targetNamespace: "notion" + trustedHeaders: + - "Authorization" + - "Notion-Version" + + consumes: + - type: "http" + namespace: "notion" + description: "Notion public API" + baseUri: "https://api.notion.com/v1" + resources: + - name: "all" + path: "/{path}" + operations: + - name: "any" + method: "GET" +``` + +### 4.2 Simple-mode capability (direct call) + +A single exposed operation that directly calls a consumed operation, maps parameters with `with`, and extracts output. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + github_token: "GITHUB_TOKEN" +info: + label: "GitHub User Lookup" + description: "Exposes a simplified endpoint to retrieve GitHub user profiles" + tags: + - github + - users + created: "2026-02-01" + modified: "2026-02-01" + +capability: + exposes: + - type: "api" + port: 3000 + namespace: "app" + resources: + - path: "/users/{username}" + description: "Look up a GitHub user by username" + name: "user" + inputParameters: + - name: "username" + in: "path" + type: "string" + description: "The GitHub username to look up" + operations: + - method: "GET" + label: "Get User" + call: "github.get-user" + with: + username: "$this.app.username" + outputParameters: + - type: "string" + mapping: "$.login" + - type: "string" + mapping: "$.email" + - type: "number" + mapping: "$.id" + + consumes: + - type: "http" + namespace: "github" + description: "GitHub REST API v3" + baseUri: "https://api.github.com" + authentication: + type: "bearer" + token: "{{github_token}}" + resources: + - name: "users" + path: "/users/{username}" + label: "Users" + operations: + - name: "get-user" + label: "Get User" + method: "GET" + inputParameters: + - name: "username" + in: "path" + outputParameters: + - name: "login" + type: "string" + value: "$.login" + - name: "email" + type: "string" + value: "$.email" + - name: "id" + type: "number" + value: "$.id" +``` + +### 4.3 Orchestrated capability (multi-step call) + +An exposed operation that chains two consumed operations using named steps and `with`. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + notion_token: "NOTION_TOKEN" +info: + label: "Database Inspector" + description: "Retrieves a Notion database then queries its contents in a single exposed operation" + tags: + - notion + - orchestration + created: "2026-02-10" + modified: "2026-02-10" + +capability: + exposes: + - type: "api" + port: 9090 + namespace: "inspector" + resources: + - path: "/databases/{database_id}/summary" + description: "Returns database metadata and first page of results" + name: "db-summary" + inputParameters: + - name: "database_id" + in: "path" + type: "string" + description: "The Notion database ID" + operations: + - name: "get-summary" + method: "GET" + label: "Get Database Summary" + steps: + - type: "call" + name: "fetch-db" + call: "notion.get-database" + with: + database_id: "$this.inspector.database_id" + - type: "call" + name: "query-db" + call: "notion.query-database" + with: + database_id: "$this.inspector.database_id" + mappings: + - targetName: "db_name" + value: "$.fetch-db.dbName" + - targetName: "row_count" + value: "$.query-db.resultCount" + outputParameters: + - name: "db_name" + type: "string" + - name: "row_count" + type: "number" + + consumes: + - type: "http" + namespace: "notion" + description: "Notion public API" + baseUri: "https://api.notion.com/v1" + authentication: + type: "bearer" + token: "{{notion_token}}" + inputParameters: + - name: "Notion-Version" + in: "header" + value: "2022-06-28" + resources: + - name: "databases" + path: "/databases/{database_id}" + label: "Databases" + operations: + - name: "get-database" + label: "Get Database" + method: "GET" + inputParameters: + - name: "database_id" + in: "path" + outputParameters: + - name: "dbName" + type: "string" + value: "$.title[0].text.content" + - name: "dbId" + type: "string" + value: "$.id" + - name: "queries" + path: "/databases/{database_id}/query" + label: "Database queries" + operations: + - name: "query-database" + label: "Query Database" + method: "POST" + inputParameters: + - name: "database_id" + in: "path" + outputParameters: + - name: "resultCount" + type: "number" + value: "$.results.length()" + - name: "results" + type: "array" + value: "$.results" +``` + +### 4.4 Orchestrated capability with lookup step + +Demonstrates a `lookup` step that cross-references the output of a previous call to enrich data. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + hr_api_key: "HR_API_KEY" +info: + label: "Team Member Resolver" + description: "Resolves team member details by matching email addresses from a project tracker" + tags: + - hr + - lookup + created: "2026-02-15" + modified: "2026-02-15" + +capability: + exposes: + - type: "api" + port: 4000 + namespace: "team" + resources: + - path: "/resolve/{email}" + description: "Finds a team member by email and returns their profile" + name: "resolve" + inputParameters: + - name: "email" + in: "path" + type: "string" + description: "Email address to look up" + operations: + - name: "resolve-member" + method: "GET" + label: "Resolve Team Member" + steps: + - type: "call" + name: "list-members" + call: "hr.list-employees" + - type: "lookup" + name: "find-member" + index: "list-members" + match: "email" + lookupValue: "$this.team.email" + outputParameters: + - "fullName" + - "department" + - "role" + mappings: + - targetName: "name" + value: "$.find-member.fullName" + - targetName: "department" + value: "$.find-member.department" + - targetName: "role" + value: "$.find-member.role" + outputParameters: + - name: "name" + type: "string" + - name: "department" + type: "string" + - name: "role" + type: "string" + + consumes: + - type: "http" + namespace: "hr" + description: "Internal HR system API" + baseUri: "https://hr.internal.example.com/api" + authentication: + type: "apikey" + key: "X-Api-Key" + value: "{{hr_api_key}}" + placement: "header" + resources: + - name: "employees" + path: "/employees" + label: "Employees" + operations: + - name: "list-employees" + label: "List All Employees" + method: "GET" + outputParameters: + - name: "email" + type: "string" + value: "$.items[*].email" + - name: "fullName" + type: "string" + value: "$.items[*].name" + - name: "department" + type: "string" + value: "$.items[*].department" + - name: "role" + type: "string" + value: "$.items[*].role" +``` + +### 4.5 Full-featured capability (mixed modes) + +Combines forward proxy, simple-mode operations, orchestrated multi-step with lookup, and multiple consumed sources. + +```yaml +--- +naftiko: "0.4" +externalRefs: + - name: "env" + type: "environment" + keys: + notion_token: "NOTION_TOKEN" + github_token: "GITHUB_TOKEN" +info: + label: "Project Dashboard" + description: "Aggregates project data from Notion and GitHub into a unified API, with a pass-through proxy for direct access" + tags: + - dashboard + - notion + - github + created: "2026-02-20" + modified: "2026-02-20" + stakeholders: + - role: "owner" + fullName: "Jane Doe" + email: "jane.doe@example.com" + - role: "editor" + fullName: "John Smith" + email: "john.smith@example.com" + +capability: + exposes: + - type: "api" + port: 9090 + namespace: "dashboard" + resources: + # --- Forward proxy (simplest) --- + - path: "/github/{path}" + description: "Direct pass-through to the GitHub API for debugging" + forward: + targetNamespace: "github" + trustedHeaders: + - "Authorization" + + # --- Simple mode (direct call) --- + - path: "/repos/{owner}/{repo}" + description: "Retrieve a GitHub repository summary" + name: "repo" + inputParameters: + - name: "owner" + in: "path" + type: "string" + description: "Repository owner (user or organization)" + - name: "repo" + in: "path" + type: "string" + description: "Repository name" + operations: + - method: "GET" + label: "Get Repository" + call: "github.get-repo" + with: + owner: "$this.dashboard.owner" + repo: "$this.dashboard.repo" + outputParameters: + - type: "string" + mapping: "$.full_name" + - type: "number" + mapping: "$.stargazers_count" + - type: "string" + mapping: "$.language" + + # --- Orchestrated mode (multi-step call + lookup) --- + - path: "/projects/{database_id}/contributors" + description: "Lists project tasks from Notion and enriches each assignee with GitHub profile data" + name: "contributors" + inputParameters: + - name: "database_id" + in: "path" + type: "string" + description: "Notion database ID for the project tracker" + operations: + - name: "list-contributors" + method: "GET" + label: "List Project Contributors" + steps: + - type: "call" + name: "query-tasks" + call: "notion.query-database" + with: + database_id: "$this.dashboard.database_id" + - type: "call" + name: "list-github-users" + call: "github.list-org-members" + with: + org: "naftiko" + - type: "lookup" + name: "match-contributors" + index: "list-github-users" + match: "login" + lookupValue: "$.query-tasks.assignee" + outputParameters: + - "login" + - "avatar_url" + - "html_url" + mappings: + - targetName: "contributors" + value: "$.match-contributors" + outputParameters: + - name: "contributors" + type: "array" + items: + type: "object" + properties: + login: + type: "string" + avatar_url: + type: "string" + html_url: + type: "string" + + consumes: + - type: "http" + namespace: "notion" + description: "Notion public API for database and page operations" + baseUri: "https://api.notion.com/v1" + authentication: + type: "bearer" + token: "{{notion_token}}" + inputParameters: + - name: "Notion-Version" + in: "header" + value: "2022-06-28" + resources: + - name: "db-query" + path: "/databases/{database_id}/query" + label: "Database Query" + operations: + - name: "query-database" + label: "Query Database" + method: "POST" + inputParameters: + - name: "database_id" + in: "path" + outputParameters: + - name: "assignee" + type: "string" + value: "$.results[*].properties.Assignee.people[0].name" + - name: "taskName" + type: "string" + value: "$.results[*].properties.Name.title[0].text.content" + + - type: "http" + namespace: "github" + description: "GitHub REST API for repository and user operations" + baseUri: "https://api.github.com" + authentication: + type: "bearer" + token: "{{github_token}}" + resources: + - name: "repos" + path: "/repos/{owner}/{repo}" + label: "Repositories" + operations: + - name: "get-repo" + label: "Get Repository" + method: "GET" + inputParameters: + - name: "owner" + in: "path" + - name: "repo" + in: "path" + outputParameters: + - name: "full_name" + type: "string" + value: "$.full_name" + - name: "stargazers_count" + type: "number" + value: "$.stargazers_count" + - name: "language" + type: "string" + value: "$.language" + - name: "org-members" + path: "/orgs/{org}/members" + label: "Organization Members" + operations: + - name: "list-org-members" + label: "List Organization Members" + method: "GET" + inputParameters: + - name: "org" + in: "path" + outputParameters: + - name: "login" + type: "string" + value: "$[*].login" + - name: "avatar_url" + type: "string" + value: "$[*].avatar_url" + - name: "html_url" + type: "string" + value: "$[*].html_url" +``` + +--- + +## 5. Versioning + +The Naftiko Specification uses semantic versioning. The `naftiko` field in the Naftiko Object specifies the exact version of the specification (e.g., `"0.4"`). + +Tools processing Naftiko documents MUST validate this field to ensure compatibility with the specification version they support. + +--- + +This specification defines how to describe modular, composable capabilities that consume multiple sources and expose unified interfaces, supporting orchestration, authentication, and flexible routing patterns. \ No newline at end of file diff --git a/src/main/resources/wiki/Tutorial.md b/src/main/resources/wiki/Tutorial.md new file mode 100644 index 0000000..c05e8cb --- /dev/null +++ b/src/main/resources/wiki/Tutorial.md @@ -0,0 +1,353 @@ +## Introduction + +Welcome to the tutorial for Naftiko Framework. Starting with the simplest "Hello World!" capability, it offers a hands-on, progressive journey to learn some on the key features offered to do Spec-Driven Integration. + +Please read the [installation instructions](https://github.com/naftiko/framework/wiki/Installation) to know how to run the Naftiko Engine with your capability file. + +## 1. My first capability +Start with a very basic capability returning "Hello, World!", using only an API server adapter with constants. +``` +naftiko: "0.4" +capability: + exposes: + - type: "api" + port: 8081 + namespace: "tutorial" + resources: + - path: "/hello" + name: "hello" + label: "My first resource" + description: "This is a resource to demonstrate a simple Hello, World! API endpoint" + operations: + - method: "GET" + outputParameters: + - type: "string" + const: "Hello, World!" +``` +Then run the Naftiko Engine with this capability, and execute a GET request on http://localhost:8081/hello. \ +It should display the following JSON response +``` +{ + "value": "Hello, World!" +} +``` +Congrats, you ran your first capability! + +## 2. Forwarding API resource +Add a "consumes" section to forward an existing API (Notion API in our example) and expose it as a capability. +``` +naftiko: "0.4" +info: + label: "Tutorial - Step 2 - Forwarding API Resource" + +capability: + exposes: + - type: "api" + port: 8081 + namespace: "sample" + resources: + - path: "/notion/{{path}}" + description: "This resource forwards requests to the Notion API, allowing access to any Notion endpoint by specifying the path parameter. For example, a request to /notion/pages would be forwarded to https://api.notion.com/v1/pages." + forward: + targetNamespace: notion + trustedHeaders: + - Notion-Version + + consumes: + - type: "http" + description: "Forwarded requests from the /notion/{path} resource, to be sent to the Notion API" + namespace: "notion" + baseUri: "https://api.notion.com/v1/" +``` +Execute a GET request on http://localhost:8081/notion/users/me. You must add a "Authorization: Bearer your_notion_api_key" and a "Notion-Version: 2025-09-03" header. Note the {{path}} keyword that allows you to access all API resources (and not only the /users/me of this example). The value of the keyword will be paste after https://api.notion.com/v1/ \ +It should display a JSON response similar to: +``` +{ + "object": "user", + "id": "9c1061dc-0350-4aa0-afb0-xxxxxxxxxxxx", + "name": "Capability", + "avatar_url": null, + "type": "bot", + "bot": { + "owner": { + "type": "workspace", + "workspace": true + }, + "workspace_name": "Naftiko", + "workspace_id": "39d4adce-3d02-81a1-afeb-xxxxxxxxxxxx", + "workspace_limits": { + "max_file_upload_size_in_bytes": 5368709120 + } + }, + "request_id": "a3d33d51-d51f-4f3b-a616-xxxxxxxxxxxx" +} +``` + +## 3. Encapsulating Headers +Let's say you don't want to add headers when requesting the capability. For that you can define these headers as input parameters in the "consumes" section. +``` +naftiko: "0.4" +info: + label: "Tutorial - Step 3 - Encapsulating Headers" + description: "This is a sample capability specification to demonstrate the features of Naftiko" + +capability: + exposes: + - type: "api" + port: 8081 + namespace: "sample" + resources: + - path: "/notion/{{path}}" + description: "This resource forwards requests to the Notion API, allowing access to any Notion endpoint by specifying the path parameter. For example, a request to /notion/pages would be forwarded to https://api.notion.com/v1/pages." + forward: + targetNamespace: notion + + consumes: + - type: "http" + description: "Forwarded requests from the /notion/{path} resource, to be sent to the Notion API" + namespace: "notion" + baseUri: "https://api.notion.com/v1/" + authentication: + type: "bearer" + token: "{{notion_api_key}}" + inputParameters: + - name: "notion_api_key" + in: "environment" + - name: "Notion-Version" + in: "header" + const: "2025-09-03" +``` +Note that the "Notion-Version" value is a constant, whereas the "notion_api_key" value comes from an environment variable. So you have to pass this environment variable to your Framework Engine. If you use Docker with the docker run command, you can do it with the option --env notion_api_key="your_notion_api_key"\ +Even now you can request your capability without any header. + +## 4. Filter response +If you want to get a more concise response with only the fields you need. for example: id, name, and type. You can define the output parameters for a specific operation. +``` +naftiko: "0.4" +info: + label: "Tutorial - Step 4 - Filter response" + description: "This is a sample capability specification to demonstrate the features of Naftiko" + tags: + - Naftiko + - Tutorial + +capability: + exposes: + - type: "api" + port: 8081 + namespace: "sample" + resources: + - path: "/notion/users/me" + description: "This resource is for the specific path /notion/users/me and restricted to nested operations." + forward: + targetNamespace: notion + operations: + - method: "GET" + call: "notion.get-me" + outputParameters: + - type: "object" + properties: + id: + type: "string" + mapping: "$.id" + name: + type: "string" + mapping: "$.name" + type: + type: "string" + mapping: "$.type" + + consumes: + - type: "http" + description: "Forwarded requests from the /notion/{path} resource, to be sent to the Notion API" + namespace: "notion" + baseUri: "https://api.notion.com/v1/" + authentication: + type: "bearer" + token: "{{notion_api_key}}" + inputParameters: + - name: "notion_api_key" + in: "environment" + - name: "Notion-Version" + in: "header" + const: "2025-09-03" + resources: + - path: "users/me" + operations: + - method: "GET" + name: "get-me" +``` +In this case, if you execute the previous GET request on http://localhost:8081/notion/users/me, it should display a smaller JSON response similar to: +``` +{ + "id": "9c1061dc-0350-4aa0-afb0-xxxxxxxxxxxx", + "name": "Capability", + "type": "bot" +} +``` + +## 5. Multi steps +***This feature is not fully implemented in v0.4 (especially with regard to the transfer of variables between steps)***\ +You can define a specific capability that calls several endpoints to provide a consolidate result. For this example, you define a "GET my full user" capability which first call "users/me" (get me), then use the result userId to call "users/{{userId}}" (get user by id). +``` +naftiko: "0.4" +info: + label: "Tutorial - Step 5 - Multi steps" + description: "This capability is based on two resources called one after the other. The second step takes an output value of the first one as argument (user_id)" + tags: + - Naftiko + - Tutorial + created: "2026-02-26" + modified: "2026-03-04" + +capability: + exposes: + - type: "api" + port: 8081 + namespace: "my-capability" + resources: + - path: "/notion/my-full-user" + description: "This resource is for the specific path /notion/my-full-user and restricted to nested operations." + forward: + targetNamespace: notion + operations: + - method: "GET" + steps: + - name: "fetch-me-user" + type: "call" + call: "notion.get-me" + - name: "fetch-user-by-id" + type: "call" + call: "notion.get-user" + with: + user_id: "{{fetch-me-user.userid}}" + + consumes: + - type: "http" + description: "Forwarded requests from the /notion/{path} resource, to be sent to the Notion API" + namespace: "notion" + baseUri: "https://api.notion.com/v1/" + authentication: + type: "bearer" + token: "{{notion_api_key}}" + inputParameters: + - name: "notion_api_key" + in: "environment" + - name: "Notion-Version" + in: "header" + const: "2025-09-03" + - name: "user_id" + in: "path" + resources: + - path: "users/me" + name: "users-me" + operations: + - method: "GET" + name: "get-me" + outputParameters: + - name: "userid" + type: "string" + value: "$.id" + - path: "users/{{user_id}}" + name: "users-by-id" + inputParameters: + - name: "user_id" + in: "path" + operations: + - method: "GET" + name: "get-user" +``` +You can execute a GET request on http://localhost:8081/notion/my-full-user and you'll get your user infos. + +## 6. MCP +If you want to expose your capability as MCP tool, this is possible. +``` +naftiko: "0.4" +info: + label: "Tutorial - Step 6 - MCP" + description: "This is a sample capability specification to demonstrate the MCP exposition feature" + tags: + - Naftiko + - Tutorial + created: "2026-03-05" + modified: "2026-03-05" + stakeholders: + - role: "editor" + fullName: "Navi" + email: "navi@naftiko.io" + +capability: + exposes: + - type: "mcp" + address: "localhost" + port: 9091 + namespace: "notion-mcp" + description: "MCP server exposing Notion database query capabilities for pre-release participant management." + + tools: + - name: "query-database" + description: "Query the Notion pre-release participants database to retrieve committed participants with their name, company, title, location, owner, participation status and comments." + call: "notion.query-db" + with: + datasource_id: "2fe4adce-3d02-8028-bec8-000bfb5cafa2" + outputParameters: + - type: "array" + mapping: "$.results" + items: + - type: "object" + properties: + name: + type: "string" + mapping: "$.properties.Name.title[0].text.content" + company: + type: "string" + mapping: "$.properties.Company.rich_text[0].text.content" + title: + type: "string" + mapping: "$.properties.Title.rich_text[0].text.content" + location: + type: "string" + mapping: "$.properties.Location.rich_text[0].text.content" + owner: + type: "string" + mapping: "$.properties.Owner.people[0].name" + participation_status: + type: "string" + mapping: "$.properties.Participation Status.select.name" + comments: + type: "string" + mapping: "$.properties.Comments.rich_text[0].text.content" + + consumes: + - type: "http" + description: "Notion API integration for accessing pre-release participant data stored in Notion." + namespace: "notion" + baseUri: "https://api.notion.com/v1/" + authentication: + type: "bearer" + token: "{{notion_api_key}}" + inputParameters: + - name: "notion_api_key" + in: "environment" + - name: "Notion-Version" + in: "header" + const: "2025-09-03" + resources: + - path: "data_sources/{{datasource_id}}/query" + name: "query" + label: "Query database resource" + operations: + - method: "POST" + name: "query-db" + label: "Query Database" + body: | + { + "filter": { + "property": "Participation Status", + "select": { + "equals": "Committed" + } + } + } +``` +You can execute an MCP request on http://localhost:9091 and you should be able to execute the tool. \ No newline at end of file diff --git a/src/main/resources/wiki/Use-cases.md b/src/main/resources/wiki/Use-cases.md new file mode 100644 index 0000000..ee26f7b --- /dev/null +++ b/src/main/resources/wiki/Use-cases.md @@ -0,0 +1,122 @@ +Here is an overview of typical use cases where Naftiko Framework can help developers and supporting features available. Additional features are being added as described in the [roadmap](https://github.com/naftiko/framework/wiki/Roadmap). + +## 1. AI integration + +Connect AI assistants to your systems through capabilities, so they can access trusted business data and actions without custom glue code. + +How Naftiko achieves this technically: +- Declare upstream systems in `capability.consumes` (typically `type: http`) with `namespace`, `baseUri`, authentication, headers, and operation contracts. +- Expose the same domain as `type: mcp` tools and/or `type: api` resources in `capability.exposes`, so both AI agents and traditional clients use one integration layer. +- Use `call`, `with`, `steps`, and JSONPath `mapping` in `outputParameters` to return normalized, task-ready payloads instead of raw provider responses. + +![Integrate AI](https://naftiko.github.io/docs/images/technology/use_case_AI_integration.png) + +## 2. Rightsize AI context + +Expose only the context an AI task needs, reducing noise, improving relevance, and keeping prompts efficient. + +How Naftiko achieves this technically: +- Shape response payloads with typed `outputParameters` (string, object, array) and fine-grained JSONPath `mapping` expressions. +- Keep only relevant fields in exposed tool/resource schemas, while hiding irrelevant upstream fields from the AI surface. +- Attach meaningful `info.description`, tool descriptions, and tags so discovery is semantic and context quality stays high. + +![Rightsize AI context](https://naftiko.github.io/docs/images/technology/use_case_context_engineering_rightsize_ai_context.png) + +#### Key features +- [x] Declarative applied capability exposing MCP + - [x] Declarative applied capabilities with reused source capabilities +- [x] Support for key MCP transport protocols + - [x] MCP for Streaming HTTP (for remote MCP clients) + - [x] MCP for Standard IO (for local MCP clients) + +## 3. Elevate an existing API + +Wrap a current API as a capability to make it easier to discover, reuse, and consume across teams and channels. + +How Naftiko achieves this technically: +- Model the legacy/existing API once in `consumes.resources.operations` with explicit methods, paths, parameters, and body formats. +- Add a stable capability namespace and expose curated resource paths/tools that are easier to consume than vendor-native endpoints. +- Enforce schema-based validation (`capability-schema.json`) so the elevated contract remains consistent and machine-checkable. + +![Elevate existing APIs](https://naftiko.github.io/docs/images/technology/use_case_api_reusability_elevate_existing_apis.png) + +#### Key features +- [x] Pass thru source capability + - [x] One source HTTP adapter per capability + - [x] Multiple source HTTP adapters per capability +- [x] Declarative source HTTP adapter and target API adapter + - [x] Focus on source APIs with JSON payloads + - [x] Change HTTP methods to expose proper semantics + - E.g. Adapt read-only POST queries into cacheable GET queries + - [x] Convert XML, Avro, CSV payloads to JSON + +## 4. Compose AI context + +Combine data from multiple APIs into one capability to deliver richer, task-ready context to AI clients. + +How Naftiko achieves this technically: +- Register multiple consumed APIs with unique namespaces, then orchestrate cross-source calls using ordered `steps`. +- Bridge calls through step `mappings` and per-step input injection, so outputs from one source feed inputs of the next. +- Return a single composed output model via mapped `outputParameters`, giving AI clients one coherent result. + +![Rightsize AI context](https://naftiko.github.io/docs/images/technology/use_case_context_engineering_compose_ai_context.png) + +#### Key features +- [x] Declarative source HTTP adapter and target MCP adapter +- [x] Support for source capabilities + - [x] Reuse consistent APIs and JSON payloads + +## 5. Rightsize a set of microservices + +Create a simpler capability layer over many microservices to reduce client complexity and improve consistency. + +How Naftiko achieves this technically: +- Aggregate multiple microservice endpoints under one exposed namespace and a small set of business-oriented resources/tools. +- Standardize auth/header behavior and parameter handling at capability level instead of duplicating logic in every client. +- Use orchestration and output shaping to hide service fragmentation and return consistent contracts. + +![Rightsize microservices](https://naftiko.github.io/docs/images/technology/use_case_api_reusability_rightsize_microservices.png) + +#### Key features +- [x] Declarative source HTTP adapter and target API adapter + - [x] Finish support for source APIs with JSON payloads + - [x] Convert YAML, Protobuf, payloads to JSON + - [x] Support sequence of steps with calls + +## 6. Rightsize a monolith API + +Extract focused capabilities from a broad monolith API so consumers get only what they need for each use case. + +How Naftiko achieves this technically: +- Select only required monolith operations in `consumes` and remap them to narrower exposed resources/tools. +- Use output filtering/mapping to publish smaller, purpose-built payloads for each consumer scenario. +- Optionally forward selected routes with `forward.targetNamespace` to keep passthrough paths where full transformation is not needed. + +![Rightsize monolith APIs](https://naftiko.github.io/docs/images/technology/use_case_api_reusability_rightsize_monolith_apis.png) + +## 7. MCP-first design + +Design capabilities first for MCP clients, then map them to underlying APIs for a clean AI-native integration model. + +How Naftiko achieves this technically: +- Define `type: mcp` exposure with server-level description and tool-level contracts (name, description, input/output parameters). +- Support MCP transports (`http` or `stdio`) so the same capability can run in remote server mode or local IDE/agent mode. +- Wire tools to one or many consumed operations via `call` or orchestrated `steps`, without changing upstream APIs. + +![Capability-first approach](https://naftiko.github.io/docs/images/technology/use_case_context_engineering_capability_first.png) + +#### Key features +- [x] Declarative target API contract with mocking mode + - [x] Allow API server adapter with no source HTTP adapter + - [x] Use static value of output parameters as sample + +## 8. API-first design + +Start from existing APIs and define reusable capabilities on top, so API investments can power new AI and app experiences. + +How Naftiko achieves this technically: +- Begin with consumed API declarations (`baseUri`, auth, resources, operations), then incrementally add exposed API/MCP adapters. +- Reuse existing security and configuration using `externalRefs` with file/runtime resolution and injected variables (e.g., `{{token}}`). +- Add format-aware parsing and mapping (JSON, YAML, XML, CSV, Avro, Protobuf support in the framework) to normalize diverse backends. + +![Capability-first approach](https://naftiko.github.io/docs/images/technology/use_case_api_reusability_capability_first.png) From c6688a3e44e827d32dc6192f9fd7927b7f5245bb Mon Sep 17 00:00:00 2001 From: Jerome Louvel <374450+jlouvel@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:39:30 -0500 Subject: [PATCH 2/2] Update README.md --- README.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 615fc1d..4940a0c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Naftiko Framework -Welcome to Naftiko Framework, the first Open Source project for **Spec-Driven AI Integration**. It reinvents API integration for the AI era with a governed and versatile platform based on capabilities that streamlines API sprawl created by the massive SaaS and microservices growth. +Welcome to Naftiko Framework, the 1st Open Source project for **Spec-Driven AI Integration**. It reinvents API integration for the AI era with governed and versatile capabilities that streamline API sprawl from massive SaaS and microservices growth. Each capability is a coarse piece of domain that consumes existing HTTP-based APIs, converts data formats like Protocol Buffer, XML, YAML, CSV and Avro into JSON, enabling better Context Engineering and API reusability critical to AI integration. @@ -19,6 +19,30 @@ While the framework itself is developed in Java and can be extended to support n - :nut_and_bolt: [Contribute](https://github.com/naftiko/framework/wiki/Contribute) - :ocean: [FAQ](https://github.com/naftiko/framework/wiki/FAQ) +Please join the community of users and contributors in [this GitHub Discussion forum!](https://github.com/orgs/naftiko/discussions). + + + +Welcome to Naftiko Framework, the first Open Source project for **Spec-Driven AI Integration**. It reinvents API integration for the AI era with a governed and versatile platform based on capabilities that streamlines API sprawl created by the massive SaaS growth and microservices. + +Each capability is a coarse piece of domain that consumes existing HTTP-based APIs, converts into JSON data formats like Protocol Buffer, XML, YAML, CSV and Avro, enabling better Context Engineering and API reusability critical to AI integration. + + + +Capabilities are declared using **YAML** files, configuring the Naftiko Engine provided as a **Docker** container. Clients can then consume the capability via the **MCP** or **API** servers exposed. + +While the framework itself is developed in Java and can be extended to support new protocols, developers just need to know YAML, JSONPath and Mustache templates to take full advantage of it. + +- :ship: [Use cases](https://github.com/naftiko/framework/wiki/Use-Cases) +- :rowboat: [Installation](https://github.com/naftiko/framework/wiki/Installation) +- :sailboat: [Tutorial](https://github.com/naftiko/framework/wiki/Tutorial) +- :anchor: [Specification](https://github.com/naftiko/framework/wiki/Specification) +- :mega: [Releases](https://github.com/naftiko/framework/wiki/Releases) +- :telescope: [Roadmap](https://github.com/naftiko/framework/wiki/Roadmap) +- :nut_and_bolt: [Contribute](https://github.com/naftiko/framework/wiki/Contribute) +- :ocean: [FAQ](https://github.com/naftiko/framework/wiki/FAQ) + + Please join the community of users and contributors in [this GitHub Discussion forum!](https://github.com/orgs/naftiko/discussions).