create-lykomi-app 0.0.0-stage → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -2
  3. package/dist/index.mjs +25 -0
  4. package/package.json +50 -3
  5. package/presets/foundry-iq/files/AGENTS.md +72 -0
  6. package/presets/foundry-iq/files/README.md +145 -0
  7. package/presets/foundry-iq/files/agent/_env.example +22 -0
  8. package/presets/foundry-iq/files/agent/app/agent.py +26 -0
  9. package/presets/foundry-iq/files/agent/app/foundry_iq.py +380 -0
  10. package/presets/foundry-iq/files/agent/app/main.py +79 -0
  11. package/presets/foundry-iq/files/agent/pyproject.toml +20 -0
  12. package/presets/foundry-iq/files/src/main.tsx +27 -0
  13. package/presets/foundry-iq/files/src/pages/chat-page.tsx +11 -0
  14. package/presets/foundry-iq/files/src/tools/foundry-iq-module.tsx +7 -0
  15. package/presets/foundry-iq/files/src/tools/search-results-tool-ui.tsx +854 -0
  16. package/presets/foundry-iq/manifest.json +27 -0
  17. package/presets/neo4j/files/AGENTS.md +90 -0
  18. package/presets/neo4j/files/CLAUDE.md +1 -0
  19. package/presets/neo4j/files/README.md +187 -0
  20. package/presets/neo4j/files/agent/_env.example +44 -0
  21. package/presets/neo4j/files/agent/app/__init__.py +12 -0
  22. package/presets/neo4j/files/agent/app/agent.py +62 -0
  23. package/presets/neo4j/files/agent/app/graph_db.py +276 -0
  24. package/presets/neo4j/files/agent/app/graph_payload.py +323 -0
  25. package/presets/neo4j/files/agent/app/graph_tools.py +626 -0
  26. package/presets/neo4j/files/agent/app/main.py +75 -0
  27. package/presets/neo4j/files/agent/pyproject.toml +40 -0
  28. package/presets/neo4j/files/agent/tests/conftest.py +35 -0
  29. package/presets/neo4j/files/agent/tests/test_graph_live.py +395 -0
  30. package/presets/neo4j/files/agent/tests/test_graph_payload.py +131 -0
  31. package/presets/neo4j/files/agent/tests/test_graph_tools.py +145 -0
  32. package/presets/neo4j/files/docker-compose.yml +38 -0
  33. package/presets/neo4j/files/scripts/seed_graph.py +222 -0
  34. package/presets/neo4j/files/src/index.css +30 -0
  35. package/presets/neo4j/files/src/main.tsx +43 -0
  36. package/presets/neo4j/files/src/pages/chat-page.tsx +11 -0
  37. package/presets/neo4j/files/src/tools/graph-module.tsx +103 -0
  38. package/presets/neo4j/files/vite.config.ts +118 -0
  39. package/presets/neo4j/manifest.json +67 -0
  40. package/template/AGENTS.md +73 -0
  41. package/template/CLAUDE.md +1 -0
  42. package/template/README.md +199 -0
  43. package/template/_gitignore +17 -0
  44. package/template/agent/_env.example +9 -0
  45. package/template/agent/app/__init__.py +1 -0
  46. package/template/agent/app/agent.py +43 -0
  47. package/template/agent/app/main.py +62 -0
  48. package/template/agent/app/tools.py +26 -0
  49. package/template/agent/pyproject.toml +21 -0
  50. package/template/eslint.config.js +24 -0
  51. package/template/index.html +13 -0
  52. package/template/package.json +61 -0
  53. package/template/pnpm-workspace.yaml +20 -0
  54. package/template/public/favicon.svg +4 -0
  55. package/template/src/index.css +30 -0
  56. package/template/src/main.tsx +45 -0
  57. package/template/src/pages/chat-page.tsx +11 -0
  58. package/template/src/pages/index.tsx +21 -0
  59. package/template/src/vite-env.d.ts +16 -0
  60. package/template/tsconfig.app.json +26 -0
  61. package/template/tsconfig.json +4 -0
  62. package/template/tsconfig.node.json +15 -0
  63. package/template/vite.config.ts +100 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iLoveAgents, a brand of Leitwolf GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,96 @@
1
- # Temporary Holding Version
1
+ # create-lykomi-app
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Create a Foundry UI agent app: React chat UI, AG-UI protocol, Microsoft Agent Framework, and [Microsoft Foundry](https://learn.microsoft.com/azure/foundry/).
4
+
5
+ Foundry UI is built by [iLoveAgents](https://iloveagents.ai).
6
+
7
+ ## Create An App
8
+
9
+ ```bash
10
+ npm create lykomi-app my-agent-app
11
+ cd my-agent-app
12
+ cp agent/.env.example agent/.env
13
+ az login
14
+ pnpm dev
15
+ ```
16
+
17
+ Open `http://localhost:8010`.
18
+
19
+ Set these in `agent/.env`:
20
+
21
+ ```bash
22
+ FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
23
+ FOUNDRY_MODEL=gpt-5.4
24
+ ```
25
+
26
+ The generated app starts:
27
+
28
+ - UI: `http://localhost:8010`
29
+ - Agent service: `http://localhost:8020`
30
+ - AG-UI endpoint: `/api/agent`
31
+
32
+ The first screen is the chat. The bundled agent can call `hello_world` and `get_weather`.
33
+
34
+ ## Theme The App
35
+
36
+ Generated apps default to `Foundry UI` for both the shell title and browser title. Change branding, logos, colors, radius, and fonts through the `theme` layers in `src/main.tsx`.
37
+
38
+ ## Two Ways To Use It
39
+
40
+ | Mode | Use when | Update story |
41
+ | ---------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
42
+ | Package-backed app | You want central Foundry UI fixes and a small local app surface. | Keep the generated app and update `@iloveagents/foundry-*` packages with your package manager. |
43
+ | GitHub template / copy | You want to fully own and reshape the app. | Copy the generated app into your own template repo. You can customize everything, but you manually pull upstream ideas. |
44
+
45
+ The package-backed path updates shared UI, shell, theme resources, and AG-UI transport through npm packages. It does not overwrite local files like `agent/app/tools.py`, `src/main.tsx`, or your README.
46
+
47
+ ## Requirements
48
+
49
+ - Node.js 22+
50
+ - pnpm
51
+ - uv
52
+ - Azure CLI for local `az login`
53
+
54
+ ## Add Tools
55
+
56
+ Add Agent Framework tools in `agent/app/tools.py` and include them in `AGENT_TOOLS`.
57
+
58
+ ```python
59
+ AGENT_TOOLS = [hello_world, get_weather, my_new_tool]
60
+ ```
61
+
62
+ The generated `AGENTS.md` explains the local project contract for coding agents. `CLAUDE.md` imports it for Claude Code.
63
+
64
+ ## Options
65
+
66
+ | Flag | Description |
67
+ | ----------------- | -------------------------------------------- |
68
+ | `--preset <name>` | Preset to scaffold from. Default: `default`. |
69
+ | `--no-install` | Skip dependency install. |
70
+ | `--force` | Scaffold into a non-empty directory. |
71
+
72
+ ## Presets
73
+
74
+ | Preset | What you get |
75
+ | ------------ | --------------------------------------------------------------------------------------- |
76
+ | `default` | Chat-first Foundry UI app with FastAPI Agent Framework service and hello/weather tools. |
77
+ | `foundry-iq` | Default plus an Azure AI Search Foundry IQ knowledge base retrieval tool. |
78
+
79
+ ## Packages
80
+
81
+ The starter consumes the Foundry UI packages from npm:
82
+
83
+ - `@iloveagents/foundry-web-shell` - browser shell and `bootstrapShell`
84
+ - `@iloveagents/foundry-web-ui` - chat UI, AG-UI adapter, tool panels, theme resource
85
+ - `@iloveagents/foundry-web-primitives` - shared UI primitives
86
+ - `@iloveagents/foundry-agent` - AG-UI client transport
87
+
88
+ The packages publish built ESM JavaScript and `.d.ts` declarations. The
89
+ generated Vite app is already configured to scan their Tailwind classes from the
90
+ package `dist` folders.
91
+
92
+ Source: [`lykomi/ui`](https://github.com/lykomi/ui).
93
+
94
+ ## License
95
+
96
+ MIT
package/dist/index.mjs ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ import{copyFileSync as e,existsSync as t,mkdirSync as n,readFileSync as r,readdirSync as i,statSync as a,writeFileSync as o}from"node:fs";import{basename as s,dirname as c,join as l,resolve as u}from"node:path";import{spawnSync as d}from"node:child_process";import*as f from"@clack/prompts";import{readFile as p}from"node:fs/promises";import{fileURLToPath as m}from"node:url";const h=new Set([`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.md`,`.css`,`.html`,`.yml`,`.yaml`,`.toml`,`.env`,`.sh`,`.py`]);function g(e){let t=e.lastIndexOf(`.`);return t===-1?!0:h.has(e.slice(t))}function _(t){let{srcDir:s,dstDir:c,projectName:u,foundryPackageVersion:d,rewriteRootPackageJson:f}=t;function p(t,s,c){for(let m of i(t)){let i=l(t,m),h=a(i),_=l(s,m.startsWith(`_`)&&!m.startsWith(`__`)&&m!==`_`?`.`+m.slice(1):m);if(h.isDirectory()){n(_,{recursive:!0}),p(i,_,c+1);continue}if(f&&c===0&&m===`package.json`){let e=JSON.parse(r(i,`utf-8`));e.name=u,d&&C(e,d),o(_,JSON.stringify(e,null,2)+`
3
+ `);continue}if(g(m)){o(_,r(i,`utf-8`).split(`__PROJECT_NAME__`).join(u));continue}e(i,_)}}p(s,c,0)}async function v(e){_({srcDir:e.sourceDir,dstDir:e.targetDir,projectName:e.projectName,foundryPackageVersion:e.foundryPackageVersion,rewriteRootPackageJson:!0})}async function y(e){_({srcDir:e.overlayDir,dstDir:e.targetDir,projectName:e.projectName,rewriteRootPackageJson:!1})}async function b(e){if(!e.extraDependencies&&!e.extraDevDependencies&&!e.extraScripts)return;let t=l(e.targetDir,`package.json`),n=JSON.parse(r(t,`utf-8`));e.extraDependencies&&(n.dependencies=x({...n.dependencies??{},...e.extraDependencies})),e.extraDevDependencies&&(n.devDependencies=x({...n.devDependencies??{},...e.extraDevDependencies})),e.extraScripts&&(n.scripts={...n.scripts??{},...e.extraScripts}),o(t,JSON.stringify(n,null,2)+`
4
+ `)}function x(e){let t={};for(let n of Object.keys(e).sort())t[n]=e[n];return t}const S=[`@iloveagents/foundry-agent`,`@iloveagents/foundry-web-graph`,`@iloveagents/foundry-web-primitives`,`@iloveagents/foundry-web-shell`,`@iloveagents/foundry-web-ui`];function C(e,t){for(let n of[`dependencies`,`devDependencies`]){let r=e[n];if(!r||typeof r!=`object`||Array.isArray(r))continue;let i=r;for(let e of S)e in i&&(i[e]=t)}}async function w(e){if(e===`default`)return{name:e,templateDir:T()};if(e===`foundry-iq`||e===`neo4j`)return await D({name:e,presetRoot:E(e)});throw Error(`Unknown preset: ${e}`)}function T(){let e=c(m(import.meta.url)),n=e;for(let e=0;e<5;e++){let e=l(n,`template`);if(t(e))return e;let r=c(n);if(r===n)break;n=r}throw Error(`Could not locate bundled template/ relative to ${e}. Did you build the CLI? Run \`pnpm build\` in packages/create-foundry-ui-app.`)}function E(e){let n=c(m(import.meta.url)),r=n;for(let n=0;n<5;n++){let n=l(r,`presets`,e);if(t(n))return n;let i=c(r);if(i===r)break;r=i}throw Error(`Could not locate bundled presets/${e}/ relative to ${n}. Did you build the CLI? Run \`pnpm build\` in packages/create-foundry-ui-app.`)}async function D(e){let n=l(e.presetRoot,`manifest.json`);if(!t(n))throw Error(`Bundled preset '${e.name}' has no manifest.json.`);let r=JSON.parse(await p(n,`utf-8`));return O(e.name,e.presetRoot,r)}function O(e,n,r){if(r.extends&&r.extends!==`default`)throw Error(`Preset '${e}' extends '${r.extends}', but only 'default' is supported.`);let i=r.filesDir??`files`,a=l(n,i);if(!t(a))throw Error(r.filesDir===void 0?`Preset '${e}' has no '${i}/' directory at the package root (default filesDir).`:`Preset '${e}' declares filesDir='${i}' but the directory is missing in the package.`);return{name:e,templateDir:T(),overlayDir:a,extraDependencies:r.extraDependencies,extraDevDependencies:r.extraDevDependencies,extraScripts:r.extraScripts,envVars:r.envVars,postInstall:r.postInstall}}function k(e){let t={targetDir:null,preset:`default`,install:!0,force:!1,help:!1};for(let n=0;n<e.length;n++){let r=e[n];switch(r){case`-h`:case`--help`:t.help=!0;break;case`--no-install`:t.install=!1;break;case`--force`:t.force=!0;break;case`--preset`:t.preset=e[++n]??`default`;break;default:if(r.startsWith(`--`))throw Error(`Unknown flag: ${r}`);t.targetDir===null&&(t.targetDir=r)}}return t}function A(){console.log(`Usage: create-lykomi-app <target-dir> [options]
5
+
6
+ Scaffold a new LYKOMI app — AG-UI protocol + Microsoft Agent Framework + Azure Foundry.
7
+
8
+ Options:
9
+ --preset <name> Preset to scaffold from (default: "default" — OSS shell + AG-UI agent service).
10
+ Use "foundry-iq" for an Azure AI Search Foundry IQ knowledge base demo,
11
+ or "neo4j" for an agent over a Neo4j graph with an interactive
12
+ visualization (local database or Neo4j Aura).
13
+ --no-install Skip running pnpm install in the new directory.
14
+ --force Allow scaffolding into a non-empty directory.
15
+ -h, --help Show this message.
16
+
17
+ Example:
18
+ npx create-lykomi-app my-app
19
+ npx create-lykomi-app my-app --preset foundry-iq
20
+ npx create-lykomi-app my-app --preset neo4j
21
+ `)}function j(e){if(!e||e.length===0)return;let t=e.filter(e=>e.required),n=e.filter(e=>!e.required);t.length>0&&f.log.info(`Required env vars (set before running the app):
22
+ `+t.map(e=>` - ${e.name}: ${e.description}`).join(`
23
+ `)),n.length>0&&f.log.info(`Optional env vars:
24
+ `+n.map(e=>` - ${e.name}: ${e.description}`).join(`
25
+ `))}function M(e){let t=l(e,`package.json`),n=JSON.parse(r(t,`utf-8`)).dependencies?.[`@iloveagents/foundry-agent`];if(!n)throw Error(`Template package.json is missing @iloveagents/foundry-agent.`);if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(n))throw Error(`Template @iloveagents/foundry-agent must use an exact version, got '${n}'.`);return n}async function N(){let e=k(process.argv.slice(2));if(e.help)return A(),0;let r=e.targetDir;if(!r){let e=await f.text({message:`Project directory:`,placeholder:`my-foundry-app`,validate:e=>!e||e.length===0?`Required`:void 0});if(f.isCancel(e))return f.cancel(`Cancelled.`),1;r=String(e)}let a=u(process.cwd(),r),o=t(a);if(o&&!e.force&&i(a).length>0)return f.log.error(`Target ${a} is not empty. Use --force to scaffold over it.`),1;o||n(a,{recursive:!0}),f.log.step(`Scaffolding into ${a}`),f.log.step(`Preset: ${e.preset}`);let c=await w(e.preset),l=s(a).replace(/^[^a-zA-Z]+/,``).replace(/[^a-zA-Z0-9-_]/g,`-`)||`my-app`;if(await v({sourceDir:c.templateDir,targetDir:a,projectName:l,foundryPackageVersion:M(c.templateDir)}),c.overlayDir&&await y({overlayDir:c.overlayDir,targetDir:a,projectName:l}),await b({targetDir:a,extraDependencies:c.extraDependencies,extraDevDependencies:c.extraDevDependencies,extraScripts:c.extraScripts}),f.log.success(`Files copied (preset: ${c.name}).`),e.install){let e=f.spinner();e.start(`pnpm install`);let t=d(`pnpm`,[`install`],{cwd:a,stdio:`inherit`});if(t.status!==0)return e.error(`pnpm install failed`),f.log.warn("Scaffold completed but install failed. cd into the dir and re-run `pnpm install`."),t.status??1;e.stop(`pnpm install done`)}else f.log.info(`Skipped pnpm install (--no-install).`);return j(c.envVars),f.outro(`✓ Done. Next:\n cd ${r}\n pnpm dev`),0}N().then(e=>process.exit(e),e=>{console.error(e instanceof Error?e.message:e),process.exit(1)});export{};
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "create-lykomi-app",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.9.1",
4
+ "license": "MIT",
5
+ "description": "Scaffold a new LYKOMI app — AG-UI protocol + Microsoft Agent Framework + Azure Foundry, ready to run.",
6
+ "keywords": [
7
+ "create-app",
8
+ "foundry-ui",
9
+ "ag-ui",
10
+ "agent-framework",
11
+ "azure-foundry",
12
+ "scaffold"
13
+ ],
14
+ "type": "module",
15
+ "main": "./dist/index.mjs",
16
+ "bin": {
17
+ "create-lykomi-app": "./dist/index.mjs"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "presets",
22
+ "template",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/lykomi/ui.git",
29
+ "directory": "packages/create-foundry-ui-app"
30
+ },
31
+ "homepage": "https://github.com/lykomi/ui/tree/main/packages/create-foundry-ui-app#readme",
32
+ "bugs": "https://github.com/lykomi/ui/issues",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "engines": {
37
+ "node": ">=22"
38
+ },
39
+ "dependencies": {
40
+ "@clack/prompts": "^1.2.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^24.10.1",
44
+ "tsdown": "^0.21.10",
45
+ "typescript": "~5.9.3",
46
+ "vitest": "^4.1.4"
47
+ },
48
+ "scripts": {
49
+ "build": "tsdown",
50
+ "test:unit": "vitest run",
51
+ "typecheck": "tsc --noEmit"
52
+ }
6
53
  }
@@ -0,0 +1,72 @@
1
+ This is a Foundry UI starter app with Foundry IQ retrieval: React chat UI, AG-UI transport, Microsoft Agent Framework, and an Azure AI Search knowledge base MCP tool.
2
+
3
+ # Architecture
4
+
5
+ ```text
6
+ src/main.tsx Foundry UI shell bootstrap
7
+ src/pages/chat-page.tsx Chat screen and Foundry IQ starter prompts
8
+ src/index.css Tailwind v4 plus @iloveagents/foundry-web-ui/styles.css
9
+ src/tools/ Foundry IQ search result tool card and ChatModule registration
10
+ agent/app/main.py FastAPI app, AG-UI endpoint, and request header capture
11
+ agent/app/agent.py FoundryChatClient + Agent configuration
12
+ agent/app/foundry_iq.py Foundry IQ MCP retrieval tool
13
+ agent/app/tools.py Local Agent Framework tools; update AGENT_TOOLS here
14
+ ```
15
+
16
+ # Run
17
+
18
+ ```bash
19
+ pnpm install
20
+ cp agent/.env.example agent/.env
21
+ az login
22
+ pnpm dev
23
+ ```
24
+
25
+ The UI runs on `http://localhost:8010`. The agent service runs on `http://localhost:8020`; Vite proxies `/api/agent` to it.
26
+
27
+ # Environment
28
+
29
+ Set these in `agent/.env`:
30
+
31
+ ```bash
32
+ FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
33
+ FOUNDRY_MODEL=gpt-5.4
34
+ AZURE_AI_SEARCH_ENDPOINT=https://<search-service>.search.windows.net
35
+ FOUNDRY_IQ_KNOWLEDGE_BASE=<knowledge-base-name>
36
+ ```
37
+
38
+ `FoundryChatClient` reads its Microsoft Agent Framework env vars directly. Do not add duplicate manual env probing unless Agent Framework removes that behavior.
39
+
40
+ # Foundry IQ
41
+
42
+ - The Foundry IQ MCP endpoint is derived as `${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/mcp?api-version=2026-05-01-preview`.
43
+ - The MCP tool name defaults to `knowledge_base_retrieve`.
44
+ - The activity viewer uses the documented retrieve action with `includeActivity: true`; never synthesize activity rows.
45
+ - `agent/app/foundry_iq.py` uses Azure AI Search AAD auth with `DefaultAzureCredential`.
46
+ - For remote SharePoint knowledge sources, forward the signed-in user's token per request through `x-ms-query-source-authorization` or `authorization` on `/api/agent` to both MCP and retrieve calls.
47
+ - Hosted Foundry Agent Service MCP headers are static in the current preview. Do not claim that a hosted agent can vary MCP headers per request.
48
+
49
+ # Agent Tools
50
+
51
+ - Add local tools in `agent/app/tools.py`.
52
+ - Decorate local tools with `@tool(approval_mode="never_require")` unless you intentionally add an approval flow.
53
+ - Add new local tools to `AGENT_TOOLS`; otherwise the agent cannot call them.
54
+ - Keep the Foundry IQ tool in `agent/app/foundry_iq.py` unless you are changing retrieval behavior.
55
+
56
+ # UI
57
+
58
+ - Keep the starter chat-first. Do not add landing pages, marketing heroes, or domain-specific clutter.
59
+ - Configure quick starts in `src/pages/chat-page.tsx`.
60
+ - Keep the `foundry_iq_search` tool card and activity viewer in `src/tools/search-results-tool-ui.tsx`. If the tool output shape changes, normalize it there instead of leaking Foundry IQ-specific parsing into the shared package.
61
+ - Configure app name, browser title, logos, colors, radius, and fonts through `theme` layers in `src/main.tsx`.
62
+ - Keep `@source "../node_modules/@iloveagents/.../dist"` entries in `src/index.css`; Tailwind v4 needs them to scan package component classes.
63
+ - Keep `@import "@iloveagents/foundry-web-ui/styles.css";`; it provides the Foundry UI theme tokens.
64
+
65
+ # Checks
66
+
67
+ ```bash
68
+ pnpm typecheck
69
+ pnpm build
70
+ ```
71
+
72
+ Use browser verification for layout or chat-flow changes.
@@ -0,0 +1,145 @@
1
+ # __PROJECT_NAME__
2
+
3
+ Foundry UI starter with a Microsoft Agent Framework service, AG-UI chat, and a Foundry IQ knowledge base tool.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ pnpm install
9
+ cp agent/.env.example agent/.env
10
+ az login
11
+ pnpm dev
12
+ ```
13
+
14
+ Open `http://localhost:8010`.
15
+
16
+ `pnpm dev` starts:
17
+
18
+ - UI: `http://localhost:8010`
19
+ - Agent service: `http://localhost:8020`
20
+ - AG-UI endpoint: `/api/agent` proxied to the agent service
21
+
22
+ ## Configure Foundry
23
+
24
+ Edit `agent/.env`:
25
+
26
+ ```bash
27
+ FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
28
+ FOUNDRY_MODEL=gpt-5.4
29
+ ```
30
+
31
+ `FoundryChatClient` reads the Microsoft Agent Framework environment variables directly. The starter passes `DefaultAzureCredential`, so local development works with `az login`.
32
+
33
+ ## Configure Foundry IQ
34
+
35
+ Set the Azure AI Search endpoint and knowledge base name:
36
+
37
+ ```bash
38
+ AZURE_AI_SEARCH_ENDPOINT=https://<search-service>.search.windows.net
39
+ FOUNDRY_IQ_KNOWLEDGE_BASE=<knowledge-base-name>
40
+ ```
41
+
42
+ The tool derives the MCP endpoint documented for Foundry IQ:
43
+
44
+ ```text
45
+ ${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/mcp?api-version=2026-05-01-preview
46
+ ```
47
+
48
+ The MCP tool name defaults to `knowledge_base_retrieve`. Override only if your service exposes a different tool:
49
+
50
+ ```bash
51
+ FOUNDRY_IQ_MCP_TOOL_NAME=knowledge_base_retrieve
52
+ ```
53
+
54
+ For the activity viewer, the starter also calls the documented Azure AI Search retrieve action with `includeActivity: true`:
55
+
56
+ ```text
57
+ ${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/retrieve?api-version=2026-05-01-preview
58
+ ```
59
+
60
+ This is real service activity data from the `activity` array. Disable the extra diagnostic call only when you want MCP-only retrieval:
61
+
62
+ ```bash
63
+ FOUNDRY_IQ_INCLUDE_ACTIVITY=false
64
+ ```
65
+
66
+ ## Security Trimming
67
+
68
+ For remote SharePoint knowledge sources, Foundry IQ requires the `x-ms-query-source-authorization` header. In this starter, `agent/app/main.py` captures the incoming request's `x-ms-query-source-authorization` or `authorization` header and `agent/app/foundry_iq.py` forwards it per request to the MCP endpoint and the retrieve action.
69
+
70
+ For local development without a signed-in UI user, you can set:
71
+
72
+ ```bash
73
+ FOUNDRY_IQ_QUERY_SOURCE_AUTHORIZATION=Bearer <user-token>
74
+ ```
75
+
76
+ Do not use that fallback for production multi-user apps. Hosted Foundry Agent Service MCP headers are static in the current preview; for per-user security trimming, keep the per-request forwarding in your app/API layer.
77
+
78
+ ## Try It
79
+
80
+ The first screen is the chat. Try:
81
+
82
+ - `Search Foundry IQ for a knowledge base summary`
83
+ - `What sources are available in the knowledge base?`
84
+ - `Say hello world using the hello_world tool`
85
+
86
+ The Foundry IQ tool lives in `agent/app/foundry_iq.py`; normal demo tools live in `agent/app/tools.py`.
87
+
88
+ The chat renders `foundry_iq_search` with a search-results tool card and an expandable activity viewer for tuning query planning, search-source calls, token usage, timings, and result counts. The UI lives in:
89
+
90
+ ```text
91
+ src/tools/search-results-tool-ui.tsx
92
+ src/tools/foundry-iq-module.tsx
93
+ ```
94
+
95
+ Use that pattern for other search/retrieval tools: create a `makeAssistantToolUI` for the tool name, normalize the tool payload in the card, then register it through a `ChatModule` in `src/main.tsx`.
96
+
97
+ ## Add A Tool
98
+
99
+ Add a function in `agent/app/tools.py` and include it in `AGENT_TOOLS`:
100
+
101
+ ```python
102
+ from typing import Annotated
103
+
104
+ from agent_framework import tool
105
+ from pydantic import Field
106
+
107
+
108
+ @tool(approval_mode="never_require")
109
+ def get_time(
110
+ city: Annotated[str, Field(description="The city to report time for.")],
111
+ ) -> str:
112
+ """Return a demo time response."""
113
+ return f"It is demo time in {city}."
114
+
115
+
116
+ AGENT_TOOLS = [hello_world, get_weather, get_time]
117
+ ```
118
+
119
+ Restart `pnpm dev` after changing Python dependencies or environment variables. Tool code itself reloads through Uvicorn.
120
+
121
+ ## Project Map
122
+
123
+ ```text
124
+ agent/ FastAPI + Microsoft Agent Framework AG-UI endpoint
125
+ agent/app/foundry_iq.py Foundry IQ MCP retrieval tool
126
+ agent/app/tools.py Add local agent tools here
127
+ src/main.tsx Foundry UI shell bootstrap
128
+ src/pages/chat-page.tsx Chat screen and quick starts
129
+ src/tools/ Custom tool cards registered through ChatModules
130
+ src/index.css Tailwind v4 + Foundry UI theme resource and package dist scanning
131
+ ```
132
+
133
+ ## Auth
134
+
135
+ This starter uses local pass-through auth so the demo runs without an Entra app registration.
136
+
137
+ For production MSAL auth, remove `authProvider` and `agentFetch` from `src/main.tsx`, then set:
138
+
139
+ ```bash
140
+ VITE_MSAL_CLIENT_ID=<client-id>
141
+ VITE_MSAL_AUTHORITY=https://login.microsoftonline.com/<tenant-id>
142
+ VITE_MSAL_API_SCOPE=api://<api-app-id>/access_as_user
143
+ ```
144
+
145
+ Ensure your authenticated fetch path forwards the user's bearer token to `/api/agent`.
@@ -0,0 +1,22 @@
1
+ # Copy to agent/.env and fill these values for Microsoft Agent Framework.
2
+ FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com/api/projects/your-project
3
+ FOUNDRY_MODEL=gpt-5.4
4
+
5
+ # Foundry IQ MCP endpoint inputs.
6
+ # The app derives:
7
+ # ${AZURE_AI_SEARCH_ENDPOINT}/knowledgebases/${FOUNDRY_IQ_KNOWLEDGE_BASE}/mcp?api-version=2026-05-01-preview
8
+ # and uses the documented retrieve action with includeActivity=true for real activity diagnostics.
9
+ AZURE_AI_SEARCH_ENDPOINT=https://your-search.search.windows.net
10
+ FOUNDRY_IQ_KNOWLEDGE_BASE=your-knowledge-base
11
+ # FOUNDRY_IQ_INCLUDE_ACTIVITY=true
12
+ # FOUNDRY_IQ_MAX_OUTPUT_DOCUMENTS=6
13
+
14
+ # Optional local-dev fallback for SharePoint-backed knowledge sources.
15
+ # Production should pass the signed-in user's bearer token per request instead.
16
+ # FOUNDRY_IQ_QUERY_SOURCE_AUTHORIZATION=Bearer <user-token>
17
+
18
+ # DefaultAzureCredential uses `az login` locally, service principal env vars in CI,
19
+ # and managed identity in Azure. Uncomment tenant/client values only when needed.
20
+ # AZURE_TENANT_ID=
21
+ # AZURE_CLIENT_ID=
22
+ # AZURE_CLIENT_SECRET=
@@ -0,0 +1,26 @@
1
+ from functools import lru_cache
2
+
3
+ from agent_framework import Agent
4
+ from agent_framework.foundry import FoundryChatClient
5
+ from azure.identity import DefaultAzureCredential
6
+
7
+ from .foundry_iq import FOUNDRY_IQ_TOOLS
8
+ from .tools import AGENT_TOOLS
9
+
10
+
11
+ @lru_cache(maxsize=1)
12
+ def get_agent() -> Agent:
13
+ client = FoundryChatClient(credential=DefaultAzureCredential())
14
+
15
+ return Agent(
16
+ client=client,
17
+ name="FoundryIQAgent",
18
+ instructions=(
19
+ "You are a concise assistant for a Foundry UI app. "
20
+ "Use foundry_iq_search when the user asks about knowledge-base content, "
21
+ "documents, SharePoint-backed content, or retrieval-backed answers. "
22
+ "Use hello_world when the user asks for hello world or a hello demo. "
23
+ "Use get_weather when weather is requested."
24
+ ),
25
+ tools=[*AGENT_TOOLS, *FOUNDRY_IQ_TOOLS],
26
+ )