Work in progress / testing in progress. This is an early write-up of a pattern I’m actively building and stress-testing on a self-hosted n8n. Details, schema, and specific recommendations may change as I run into real-world edge cases. Treat this as a discussion starter, not a finalised guide. I’ll update the post as testing progresses — feedback and pushback welcome.
I wanted to share a pattern I’m building on n8n self-hosted that solves a problem I kept hitting with the AI Agent node: the system prompt grows uncontrollably when you try to make a single agent good at many tasks. Embedding all instructions, examples, brand rules, and workflows in the system message burns tokens at every single turn, even when most of those instructions are irrelevant to the user’s current request.
Anthropic recently formalised a pattern called Agent Skills with progressive disclosure: the agent only sees a tiny manifest at all times, and loads the full instructions of a skill on-demand when the user request matches it. I’ve replicated this pattern entirely inside n8n.
The idea in 30 seconds
Three layers of context loading:
-
Always loaded (cheap) — a small manifest of name + description per skill, injected into the system message. Maybe 30-50 tokens per skill.
-
Loaded on match (medium) — when the LLM pattern-matches the user request against a description, it calls a load_skill tool that returns the full instructions for that skill.
-
Loaded if needed (heavy) — the loaded skill can reference further docs (examples, edge cases) which the agent fetches via a load_reference tool only if the current task requires that detail.
Result: the agent stays small in context, but can act like a specialist on dozens of tasks.
How it maps to n8n
-
Storage: a small Postgres database (separate container) with two tables — skills(name, description, content) and skill_references(skill_name, reference_name, content).
-
Main workflow: Chat Trigger → Postgres SELECT (loads the manifest) → Code (formats it as JSON for the system prompt) → AI Agent.
-
Tools (sub-workflows exposed via the Call n8n Workflow Tool node): load_skill(skill_name) and load_reference(skill_name, reference_name). Each does a parameterised SQL SELECT and returns the row.
-
The LLM populates tool parameters via the AI button in the workflow inputs, no hardcoded values.
Why Postgres instead of n8n Data Tables?
I considered Data Tables seriously. They have real strengths: zero extra infrastructure, native CSV import/export from the UI, project-scoped access, and a public REST endpoint (/datatables) for programmatic access from outside n8n. For prototyping or for catalogs of a few dozen skills they’re a perfectly defensible choice.
What pushed me to Postgres for a long-lived skill registry is the documented limit set: column types restricted to Boolean / Date / Number / String (no JSON, no VECTOR), filters limited to equality / comparison / is-empty (no LIKE, no full-text), a default 50MB total-storage cap per instance (configurable via N8N_DATA_TABLES_MAX_SIZE_BYTES on self-hosted), and Data Tables not being readable from a Code node (“Direct programmatic access to data tables from a Code node isn’t supported”, per the docs). Postgres gives me parameterised SQL, foreign keys, history triggers, and a clean upgrade path to pgvector for similarity search once the catalog grows past ~100 entries. I’ll go into the trade-offs in detail in the longer follow-up post.
Why this is interesting
It scales nicely: 5 skills or 200 skills, the cost per turn stays roughly the same. It also makes maintenance trivial — you edit a row, not a 5000-line system prompt. And the Anthropic skill format is already markdown-based, so the same content works in n8n today and would migrate cleanly to Claude Agents tomorrow.
Curious if anyone else has built something similar. Has anyone tried this with n8n Data Tables only? With pgvector? Happy to share the full schema and workflow JSON if there’s interest.
Cheers