Foundgine + Microsoft AI: a semantic execution boundary for AI agents
What is Foundgine?
Foundgine separates what a caller wants from how the application executes it.
A caller submits structured intent. Foundgine resolves that intent against an application-defined semantic model, validates the requested capabilities, applies authorization constraints, builds an execution plan, and sends the plan to a provider such as SQL or InMemory.
The result is a reusable execution boundary that can sit underneath multiple interfaces:
Intent Sources
API GraphQL Automation AI Agent
\ | | /
\ | | /
└────────┴──────────────┴───────────┘
│
▼
┌───────────────┐
│ Foundgine │
│ │
│ Semantic │
│ Authorization │
│ Planning │
│ Execution │
└───────┬───────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
SQL InMemory Providers
Architecture reference: cristianbarragan.github.io/Foundgine/docs-site/architecture
Why is this interesting for AI agents?
Modern applications increasingly have many callers: web and mobile apps, APIs, GraphQL clients, internal services, automation, and now AI agents.
An agent can determine what it wants to accomplish, but the application should remain authoritative over:
- what capabilities exist
- what the caller is allowed to do
- which data can be accessed
- which relationships can be traversed
- how mutations are validated
- how the operation is translated into execution
- what actually gets committed
Without an explicit execution boundary, it's easy for an agent integration to collapse into a scattered pattern, where every tool reimplements its own authorization, validation, and data access:
Before: AI Agent → Tool → Application code → ORM / SQL / API
After: AI Agent → Structured Intent → Semantic Model → Capability Validation
→ Authorization → Execution Plan → Provider → Result / Evidence
The question this raises: could Foundgine provide a semantic execution layer underneath an AI agent framework?
Foundgine wouldn't replace the agent framework — the framework stays responsible for reasoning, orchestration and interaction. Foundgine sits underneath it as an application-defined, strongly typed execution boundary.
This also opens up generated agent capabilities: instead of exposing arbitrary application methods or database operations to an agent, the application exposes capabilities derived from its semantic model (Application Semantic Model → Capability Graph → Agent-facing tools → Structured Intent → Foundgine).
The agent reasons about application capabilities; the application stays in control of authorization and execution. This matters most for mutations — e.g. TransferFunds, which needs tenant isolation, account ownership, account state, amount validation, authorization revalidation, idempotency, atomic execution, and audit/evidence, all enforced by the application rather than by the agent's own judgment.
Benchmark: Run5SameClient · Run5b results
The agent can request the operation. It should not get to redefine the security rules for the operation. That separation is the architectural idea behind Foundgine.
Questions for the Microsoft AI / Agent Framework / Semantic Kernel community
- Where should this boundary sit relative to Agent Framework?
- Should agent tools be generated from application capabilities?
- How should authorization be represented between an agent and the application runtime?
- Is a semantic capability graph useful to an agent?
- Could this complement existing Microsoft AI patterns rather than introducing another agent abstraction?
Open source: github.com/CristianBarragan/Foundgine
How the boundary works in practice
Since posting the original proposal above, Foundgine has evolved into a fuller semantic-policy stack. It introduces a single, application-controlled semantic execution boundary between caller intent, domain meaning, authorization rules, and the operations actually performed on data, APIs, GraphQL, MCP tools, or backend systems — so agents can propose actions, but only the application decides what's allowed, safe, and executed.
The problem: as applications expose more functionality to callers, each tool/endpoint tends to grow its own validation, authorization, query logic, and business rules. Without a shared semantic layer, each surface (application code, GraphQL, JSON, AI-generated intent) invents its own rules for what entities/fields exist, which relationships can be traversed, which filters are valid, what's authorized, and how requests become database operations — producing duplicated semantics and inconsistent security boundaries. Foundgine centralizes that into one boundary: retrieval can discover candidates and evidence, but retrieval is not authorization. The application owns identity and policy; providers execute the already-authorized artifact.
Example: alias resolution
Two callers can ask for the same thing in different words:
- Canonical: "show me overdue purchase orders from our top supplier in Texas"
- Paraphrase: "show me the overdue buys from our top seller in Texas"
In the Supply Chain semantic contract, Buy/Buys are declared aliases of PurchaseOrder, and Seller is a declared alias of Supplier. Both sentences ground onto the same canonical semantic identities before authorization or planning ever runs.
| # |
Layer |
What happens |
| 1 |
Caller intent |
The caller sends either sentence — no SQL or provider instructions. |
| 2 |
Intent representation |
The request becomes structured intent. |
| 3 |
Semantic Model |
Exposes canonical meanings and aliases: PurchaseOrder ← Buy, Buys, Supplier ← Vendor, Seller. |
| 4 |
Semantic Operation Graph |
Becomes application meaning: overdue purchase-order semantics, ranked "top supplier," Texas constraint. |
| 5 |
Retrieval |
Relational, fuzzy/full-text, BM25/search, or graph strategies propose candidates and evidence — never grant authority. |
| 6 |
Semantic Resolution |
buys → PurchaseOrder, seller → Supplier; aliases normalize to the same canonical identities. |
| 7 |
Authorization |
Application policy runs against the resolved semantic graph and caller identity. Retrieval can't bypass this. |
| 8 |
Plan Binding |
The authorized decision binds to a provider-independent execution plan. |
| 9 |
ExecutionIR |
Carries the resolved plan and its authorization provenance across the boundary. |
| 10 |
Provider |
Only now does a physical provider (e.g. PostgreSQL) receive the already-authorized artifact. |
| 11 |
Execution |
The provider executes the constrained plan; it doesn't reinterpret caller vocabulary. |
| 12 |
Evidence |
The result carries evidence of what was resolved and executed — evidence doesn't grant authority. |
Invariant: alias matching changes vocabulary, not authority. "Buys" doesn't create a new capability, and "seller" doesn't create a second supplier meaning.
Tests: SupplyChainGroundingAliasTests.cs (advanced Supply Chain sample) · SemanticAliasSynonymGroundingTests.cs (core semantics).
Example: when more than one meaning is legal
Aliases collapse different words onto one meaning; sometimes ambiguity runs the other way — the same word legally matches two different meanings, and neither the graph nor a retrieval score can break the tie alone.
Take "active customers." Both are structurally valid readings:
- a customer whose account is enabled (
Customer.AccountEnabled)
- a customer who placed a recent order (
Customer.HasRecentOrder)
A fuzzy/BM25/vector retriever can legitimately return both, with close scores (0.91 vs. 0.89). Foundgine doesn't break the tie by picking the higher score — a higher score isn't evidence of intent, and authorization can't rescue a wrong guess; a request built from the wrong meaning is still a fully authorized request, just a perfectly authorized misunderstanding.
| Stage |
What happens |
| Retrieval (fuzzy) |
Every plausible reading returns as a candidate with its own score and evidence. Retrieval only proposes. |
| Graph-constrained resolution |
Both AccountEnabled and HasRecentOrder form a legal path — a legal path proves an interpretation is possible, not that it's intended. |
| Grounding decision |
SemanticLexicalResolver.Ground compares the two paths' signatures. Neither dominates on confidence, so the outcome is GroundingOutcome.RequiresClarification: Committed stays null, both readings listed in CompetingInterpretations with their own steps, confidence, and evidence. |
| Caller chooses |
The competing meanings surface as a clarifying question — "Did you mean customers with an enabled account, or customers with a recent order?" — instead of silently executing a guess. |
| Same boundary as everyone else |
Once chosen, that interpretation goes through the same Authorization → Planning → Execution path as any other request. |
Invariant: a legal semantic path is not proof of intent. The same fail-closed mechanism applies when a resource limit (token count, search budget, timeout) stops the search before proving a single meaning (GroundingOutcome.BudgetExceeded), and when no legal interpretation exists at all (GroundingOutcome.Unresolved). Neither case falls back to a best-effort guess.
Further reading: Lexical grounding (fuzzy retrieval, resolver complexity bounds, adversarial examples) and Grounding decisions (full GroundingDecision shape, "different evidence for the same meaning" vs. "different meanings," the complete active-customers walkthrough).
Why the boundary matters
The number of independent execution surfaces is a security and maintenance multiplier. A tool-per-capability design can give an agent dozens of places where authorization, tenant filtering, and query construction are implemented differently. Foundgine centralizes the semantic decision without making a transport or database the center of the architecture.
Deeper rationale: docs/WHY-FOUNDGINE.md, docs/APPLICATION-CATEGORIES.md, docs/ARCHITECTURE.md, docs/AUTHORIZATION.md, docs/SECURITY.md, docs/AI-AGENT.md
New: exposing MCP with open intent
Foundgine.Providers.Tools.MCP is Foundgine's actual Model Context Protocol adapter — it lets an MCP client reach the same semantic execution boundary described above, instead of getting its own bespoke tool-by-tool integration. (The standalone Foundgine.MCP NuGet package is legacy and deprecated — MCP support now ships inside Foundgine.Providers, installed as dotnet add package Foundgine.Providers.Tools.MCP.) It's wired up in the sample at samples/Foundgine.SupplyChain.Advanced/MCP.Foundgine, whose Program.cs registers it with builder.Services.AddFoundgineMcp(...) and .WithTools<FoundgineMcpTools>().
The tools it actually exposes
FoundgineMcpTools (src/Foundgine.Providers/Tools/MCP/FoundgineMcpTools.cs) registers three read-side MCP tools:
| Tool |
What it does |
foundgine_capabilities |
Discovery only — returns the semantic capability contract for the current caller. Per its own description: "Discovery is descriptive; authorization is re-evaluated during execution." |
foundgine_query |
Open intent. Takes either a natural-language string (its doc example: "show customer orders") or a JSON read intent. Natural language is run through lexical grounding (SemanticLexicalReadIntentGrounder) before execution. |
foundgine_query_semantic |
Structured intent only — same JSON shape, but for callers that already resolved the semantic model themselves; skips grounding. |
A companion FoundgineMcpMutationTools class adds the write-side equivalents (foundgine_mutation_dry_run, foundgine_mutation_approve, and others) through Foundgine's separate mutation boundary.
A real MCP call
This is an actual tools/call request an MCP client would send to invoke foundgine_query. The intentJson shape (rootEntity, selections, filter, order, limit/offset/after) is the real schema JsonReadIntentAdapter parses — not a guess:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "foundgine_query",
"arguments": {
"intentJson": "{\"rootEntity\":\"Customer\",\"selections\":[{\"field\":\"Id\"},{\"field\":\"Name\"}],\"limit\":50}"
}
}
}
intentJson can also just be plain language ("show customer orders", per the tool's own description) — foundgine_query grounds that against the semantic contract first; foundgine_query_semantic skips grounding and expects the structured JSON directly.
// samples/Foundgine.SupplyChain.Advanced/MCP.Foundgine/Program.cs
app.UseWhen(
ctx => ctx.Request.Path.StartsWithSegments("/mcp"),
branch => branch.Use(async (ctx, next) =>
{
await OpenIntentDemoSecurity.PopulateSecurityContextAsync(ctx);
await next();
}));
app.MapMcp("/mcp");
— never from the MCP client's intentJson arguments.
What Foundgine does with it
// src/Foundgine.Providers/Tools/MCP/FoundgineMcpTools.cs (real, trimmed)
[McpServerTool(Name = "foundgine_query")]
public async Task<string> ExecuteQueryAsync(string intentJson, CancellationToken cancellationToken)
{
var security = _securityContextProvider.RequireSecurityExecutionContext("MCP", "execution");
var intent = ParseOpenIntent(intentJson, cancellationToken); // JSON or NL -> ReadIntent
intent = intent with { Security = security }; // host-owned, not client-supplied
var result = await _foundgine.ExecuteAsync(intent, _contextFactory(), cancellationToken);
return JsonSerializer.Serialize(new { rows = result.Rows, pageInfo = result.PageInfo,
evidence = result.Evidence, receipt = result.Receipt });
}
This calls the same IFoundgine.ExecuteAsync(ReadIntent, ...) entry point that Foundgine's in-process fluent query API compiles down to (src/Foundgine.Runtime/Query/QueryBuilder.cs):
// Typed C# — written by hand in application code
var result = await foundgine
.Query<Customer>()
.Select(c => new { c.Id, c.Name })
.Take(50)
.ExecuteAsync();
// Dynamic C# — the same shape FoundgineMcpTools builds internally from intentJson
var result = await foundgine
.Query("Customer")
.Where("Id", SemanticFilterOperator.Eq, customerId)
.Take(50)
.ExecuteAsync();
Typed C#, dynamic C#, and MCP's intentJson are three ways of authoring the same ReadIntent. Everything downstream of that — resolution → authorization → planning → execution — doesn't know or care which one produced it.
Full chain for an MCP call: MCP client → tools/call (foundgine_query, JSON-RPC) → FoundgineMcpTools → ReadIntent (host attaches SecurityExecutionContext) → IFoundgine.ExecuteAsync → resolution → authorization → planning → execution → { rows, pageInfo, evidence, receipt } back to the client.
This is what keeps an MCP-exposed agent from becoming a tool-surface liability: instead of one authorization/validation implementation per exposed tool (the "50 tools, 50 security surfaces" problem), every MCP call resolves through the one semantic + authorization boundary that GraphQL, JSON, and other callers already share.
Package shape
| Package |
Responsibility |
Foundgine.Core |
Semantic model, metadata, intent, planning and provider-independent contracts |
Foundgine.Runtime |
Application-facing orchestration, authorization and execution |
Foundgine.Providers |
Storage, AI/model, MCP, AOT and other concrete integrations |
Foundgine.Extensions |
Optional framework integrations such as Hot Chocolate GraphQL |
The normal application starting point is Foundgine.Runtime + Foundgine.Providers.
Get started
The fastest path is the Supply Chain sample pair:
Conceptual path: docs/README.md or the documentation site.
Evidence
The repository contains controlled benchmarks and deterministic security tests, distinguishing measured tool calls, latency, RPS, and success/failure counts from estimated context metrics:
Benchmark results are workload-specific and should not be generalized beyond the published experiment.
Foundgine + Microsoft AI: a semantic execution boundary for AI agents
What is Foundgine?
Foundgine separates what a caller wants from how the application executes it.
A caller submits structured intent. Foundgine resolves that intent against an application-defined semantic model, validates the requested capabilities, applies authorization constraints, builds an execution plan, and sends the plan to a provider such as SQL or InMemory.
The result is a reusable execution boundary that can sit underneath multiple interfaces:
Architecture reference: cristianbarragan.github.io/Foundgine/docs-site/architecture
Why is this interesting for AI agents?
Modern applications increasingly have many callers: web and mobile apps, APIs, GraphQL clients, internal services, automation, and now AI agents.
An agent can determine what it wants to accomplish, but the application should remain authoritative over:
Without an explicit execution boundary, it's easy for an agent integration to collapse into a scattered pattern, where every tool reimplements its own authorization, validation, and data access:
The question this raises: could Foundgine provide a semantic execution layer underneath an AI agent framework?
Foundgine wouldn't replace the agent framework — the framework stays responsible for reasoning, orchestration and interaction. Foundgine sits underneath it as an application-defined, strongly typed execution boundary.
This also opens up generated agent capabilities: instead of exposing arbitrary application methods or database operations to an agent, the application exposes capabilities derived from its semantic model (Application Semantic Model → Capability Graph → Agent-facing tools → Structured Intent → Foundgine).
The agent reasons about application capabilities; the application stays in control of authorization and execution. This matters most for mutations — e.g.
TransferFunds, which needs tenant isolation, account ownership, account state, amount validation, authorization revalidation, idempotency, atomic execution, and audit/evidence, all enforced by the application rather than by the agent's own judgment.Benchmark: Run5SameClient · Run5b results
The agent can request the operation. It should not get to redefine the security rules for the operation. That separation is the architectural idea behind Foundgine.
Questions for the Microsoft AI / Agent Framework / Semantic Kernel community
Open source: github.com/CristianBarragan/Foundgine
How the boundary works in practice
Since posting the original proposal above, Foundgine has evolved into a fuller semantic-policy stack. It introduces a single, application-controlled semantic execution boundary between caller intent, domain meaning, authorization rules, and the operations actually performed on data, APIs, GraphQL, MCP tools, or backend systems — so agents can propose actions, but only the application decides what's allowed, safe, and executed.
The problem: as applications expose more functionality to callers, each tool/endpoint tends to grow its own validation, authorization, query logic, and business rules. Without a shared semantic layer, each surface (application code, GraphQL, JSON, AI-generated intent) invents its own rules for what entities/fields exist, which relationships can be traversed, which filters are valid, what's authorized, and how requests become database operations — producing duplicated semantics and inconsistent security boundaries. Foundgine centralizes that into one boundary: retrieval can discover candidates and evidence, but retrieval is not authorization. The application owns identity and policy; providers execute the already-authorized artifact.
Example: alias resolution
Two callers can ask for the same thing in different words:
In the Supply Chain semantic contract,
Buy/Buysare declared aliases ofPurchaseOrder, andSelleris a declared alias ofSupplier. Both sentences ground onto the same canonical semantic identities before authorization or planning ever runs.PurchaseOrder ← Buy, Buys,Supplier ← Vendor, Seller.buys → PurchaseOrder,seller → Supplier; aliases normalize to the same canonical identities.Invariant: alias matching changes vocabulary, not authority. "Buys" doesn't create a new capability, and "seller" doesn't create a second supplier meaning.
Tests:
SupplyChainGroundingAliasTests.cs(advanced Supply Chain sample) ·SemanticAliasSynonymGroundingTests.cs(core semantics).Example: when more than one meaning is legal
Aliases collapse different words onto one meaning; sometimes ambiguity runs the other way — the same word legally matches two different meanings, and neither the graph nor a retrieval score can break the tie alone.
Take "active customers." Both are structurally valid readings:
Customer.AccountEnabled)Customer.HasRecentOrder)A fuzzy/BM25/vector retriever can legitimately return both, with close scores (0.91 vs. 0.89). Foundgine doesn't break the tie by picking the higher score — a higher score isn't evidence of intent, and authorization can't rescue a wrong guess; a request built from the wrong meaning is still a fully authorized request, just a perfectly authorized misunderstanding.
AccountEnabledandHasRecentOrderform a legal path — a legal path proves an interpretation is possible, not that it's intended.SemanticLexicalResolver.Groundcompares the two paths' signatures. Neither dominates on confidence, so the outcome isGroundingOutcome.RequiresClarification:Committedstays null, both readings listed inCompetingInterpretationswith their own steps, confidence, and evidence.Invariant: a legal semantic path is not proof of intent. The same fail-closed mechanism applies when a resource limit (token count, search budget, timeout) stops the search before proving a single meaning (
GroundingOutcome.BudgetExceeded), and when no legal interpretation exists at all (GroundingOutcome.Unresolved). Neither case falls back to a best-effort guess.Further reading: Lexical grounding (fuzzy retrieval, resolver complexity bounds, adversarial examples) and Grounding decisions (full
GroundingDecisionshape, "different evidence for the same meaning" vs. "different meanings," the complete active-customers walkthrough).Why the boundary matters
The number of independent execution surfaces is a security and maintenance multiplier. A tool-per-capability design can give an agent dozens of places where authorization, tenant filtering, and query construction are implemented differently. Foundgine centralizes the semantic decision without making a transport or database the center of the architecture.
Deeper rationale:
docs/WHY-FOUNDGINE.md,docs/APPLICATION-CATEGORIES.md,docs/ARCHITECTURE.md,docs/AUTHORIZATION.md,docs/SECURITY.md,docs/AI-AGENT.mdNew: exposing MCP with open intent
Foundgine.Providers.Tools.MCPis Foundgine's actual Model Context Protocol adapter — it lets an MCP client reach the same semantic execution boundary described above, instead of getting its own bespoke tool-by-tool integration. (The standaloneFoundgine.MCPNuGet package is legacy and deprecated — MCP support now ships insideFoundgine.Providers, installed asdotnet add package Foundgine.Providers.Tools.MCP.) It's wired up in the sample atsamples/Foundgine.SupplyChain.Advanced/MCP.Foundgine, whoseProgram.csregisters it withbuilder.Services.AddFoundgineMcp(...)and.WithTools<FoundgineMcpTools>().The tools it actually exposes
FoundgineMcpTools(src/Foundgine.Providers/Tools/MCP/FoundgineMcpTools.cs) registers three read-side MCP tools:foundgine_capabilitiesfoundgine_query"show customer orders") or a JSON read intent. Natural language is run through lexical grounding (SemanticLexicalReadIntentGrounder) before execution.foundgine_query_semanticA companion
FoundgineMcpMutationToolsclass adds the write-side equivalents (foundgine_mutation_dry_run,foundgine_mutation_approve, and others) through Foundgine's separate mutation boundary.A real MCP call
This is an actual
tools/callrequest an MCP client would send to invokefoundgine_query. TheintentJsonshape (rootEntity,selections,filter,order,limit/offset/after) is the real schemaJsonReadIntentAdapterparses — not a guess:{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "foundgine_query", "arguments": { "intentJson": "{\"rootEntity\":\"Customer\",\"selections\":[{\"field\":\"Id\"},{\"field\":\"Name\"}],\"limit\":50}" } } }intentJsoncan also just be plain language ("show customer orders", per the tool's own description) —foundgine_querygrounds that against the semantic contract first;foundgine_query_semanticskips grounding and expects the structured JSON directly.— never from the MCP client's
intentJsonarguments.What Foundgine does with it
This calls the same
IFoundgine.ExecuteAsync(ReadIntent, ...)entry point that Foundgine's in-process fluent query API compiles down to (src/Foundgine.Runtime/Query/QueryBuilder.cs):Typed C#, dynamic C#, and MCP's
intentJsonare three ways of authoring the sameReadIntent. Everything downstream of that — resolution → authorization → planning → execution — doesn't know or care which one produced it.Full chain for an MCP call: MCP client →
tools/call(foundgine_query, JSON-RPC) →FoundgineMcpTools→ReadIntent(host attachesSecurityExecutionContext) →IFoundgine.ExecuteAsync→ resolution → authorization → planning → execution →{ rows, pageInfo, evidence, receipt }back to the client.This is what keeps an MCP-exposed agent from becoming a tool-surface liability: instead of one authorization/validation implementation per exposed tool (the "50 tools, 50 security surfaces" problem), every MCP call resolves through the one semantic + authorization boundary that GraphQL, JSON, and other callers already share.
Package shape
Foundgine.CoreFoundgine.RuntimeFoundgine.ProvidersFoundgine.ExtensionsThe normal application starting point is
Foundgine.Runtime+Foundgine.Providers.Get started
The fastest path is the Supply Chain sample pair:
samples/Foundgine.SupplyChain— the smallest realistic application boundarysamples/Foundgine.SupplyChain.Advanced— richer semantics, grounding, retrieval, authorization and adversarial testing (start atdocs/00-Overview-And-Setup.md, follow 01–05)Conceptual path:
docs/README.mdor the documentation site.Evidence
The repository contains controlled benchmarks and deterministic security tests, distinguishing measured tool calls, latency, RPS, and success/failure counts from estimated context metrics:
benchmarks/AgentEndToEnd/README.mdBenchmark results are workload-specific and should not be generalized beyond the published experiment.