@heibergindustries/orakel-mcp 1.0.0
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.
- package/README.md +131 -0
- package/dist/server.js +350 -0
- package/package.json +42 -0
- package/skills/company-deep-dive/SKILL.md +125 -0
- package/skills/market-scan/SKILL.md +173 -0
- package/skills/ownership-map/SKILL.md +139 -0
- package/skills/prospect/SKILL.md +140 -0
- package/skills/push-to-crm/SKILL.md +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# @heibergindustries/orakel-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for **Orakel** — Norwegian company data, financials, ownership, procurement, and prospecting tools. Works with Claude Code, Claude Desktop, and Gemini CLI.
|
|
4
|
+
|
|
5
|
+
## Get Access
|
|
6
|
+
|
|
7
|
+
Request an API key at [orakel.heiberg.co](https://orakel.heiberg.co).
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
ORAKEL_API_KEY=orakel_YOUR_KEY npx -y @heibergindustries/orakel-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Setup — Claude Code
|
|
16
|
+
|
|
17
|
+
Add to `~/.claude/settings.json`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"orakel": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@heibergindustries/orakel-mcp"],
|
|
25
|
+
"env": {
|
|
26
|
+
"ORAKEL_API_KEY": "orakel_YOUR_KEY"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Setup — Claude Desktop
|
|
34
|
+
|
|
35
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"orakel": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["-y", "@heibergindustries/orakel-mcp"],
|
|
43
|
+
"env": {
|
|
44
|
+
"ORAKEL_API_KEY": "orakel_YOUR_KEY"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Setup — Gemini CLI
|
|
52
|
+
|
|
53
|
+
Add to your Gemini CLI MCP config:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"orakel": {
|
|
59
|
+
"command": "npx",
|
|
60
|
+
"args": ["-y", "@heibergindustries/orakel-mcp"],
|
|
61
|
+
"env": {
|
|
62
|
+
"ORAKEL_API_KEY": "orakel_YOUR_KEY"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Available Tools
|
|
70
|
+
|
|
71
|
+
| Tool | Description |
|
|
72
|
+
|------|-------------|
|
|
73
|
+
| `lookup_company` | Look up a company by org number |
|
|
74
|
+
| `search_companies` | Search by name, NACE, municipality, county, employees, revenue, tech stack, ad pixels |
|
|
75
|
+
| `get_financials` | Get financial data (revenue, profit, assets, equity, debt, ratios) |
|
|
76
|
+
| `find_prospects` | Find prospects by industry, location, size — formatted for sales |
|
|
77
|
+
| `get_corporate_group` | Get parent company + subsidiaries hierarchy |
|
|
78
|
+
| `get_shareholders` | Get shareholder register with ownership percentages |
|
|
79
|
+
| `get_ownership_network` | Reverse lookup: what does a company/person own? |
|
|
80
|
+
| `list_municipalities` | List Norwegian municipalities and county codes |
|
|
81
|
+
| `search_inspections` | Search Mattilsynet food safety inspections (smilefjes) |
|
|
82
|
+
| `search_licenses` | Search alcohol/tobacco licenses (TBR) |
|
|
83
|
+
| `search_procurement` | Search public procurement notices (Doffin) |
|
|
84
|
+
| `enrichment_status` | View domain enrichment pipeline results |
|
|
85
|
+
| `push_to_destination` | Push companies to CRM (Attio, webhooks) |
|
|
86
|
+
| `list_destinations` | List configured push destinations |
|
|
87
|
+
| `manage_destination` | Create/update/delete push destinations |
|
|
88
|
+
| `check_health` | Check API health, database status, sync progress |
|
|
89
|
+
|
|
90
|
+
## Skills
|
|
91
|
+
|
|
92
|
+
Pre-built guided workflows are included in the `skills/` directory of this package. Copy them to your `.claude/skills/` directory:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cp -r $(npm root -g)/@heibergindustries/orakel-mcp/skills/* ~/.claude/skills/
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Available skills: `/prospect`, `/company-deep-dive`, `/push-to-crm`, `/market-scan`, `/ownership-map`.
|
|
99
|
+
|
|
100
|
+
## Environment Variables
|
|
101
|
+
|
|
102
|
+
| Variable | Default | Description |
|
|
103
|
+
|----------|---------|-------------|
|
|
104
|
+
| `ORAKEL_API_KEY` | (required) | Your Orakel API key |
|
|
105
|
+
| `ORAKEL_URL` | `https://orakel.heiberg.co` | API base URL (override for local dev) |
|
|
106
|
+
|
|
107
|
+
## Examples
|
|
108
|
+
|
|
109
|
+
Once configured, ask from any project:
|
|
110
|
+
|
|
111
|
+
- "Look up Equinor" → `lookup_company`
|
|
112
|
+
- "Find 15 bars in Oslo with over 10M revenue" → `find_prospects`
|
|
113
|
+
- "What companies does Aker own?" → `get_ownership_network`
|
|
114
|
+
- "Show me IT consulting companies in Bergen" → `search_companies`
|
|
115
|
+
- "Push these prospects to my CRM" → `push_to_destination`
|
|
116
|
+
|
|
117
|
+
## Data Sources
|
|
118
|
+
|
|
119
|
+
All data sourced from Norwegian public registries under NLOD 2.0:
|
|
120
|
+
|
|
121
|
+
- **Brønnøysundregistrene** — Company register, roles, financials
|
|
122
|
+
- **Mattilsynet** — Food safety inspections
|
|
123
|
+
- **Helsedirektoratet** — Alcohol/tobacco licenses
|
|
124
|
+
- **Doffin** — Public procurement notices
|
|
125
|
+
- **Aksjonærregisteret** — Shareholder data
|
|
126
|
+
- **SSB** — Municipality reference data
|
|
127
|
+
- **NORID** — Domain verification
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
const ORAKEL_URL = process.env.ORAKEL_URL ?? "https://orakel.heiberg.co";
|
|
6
|
+
const ORAKEL_API_KEY = process.env.ORAKEL_API_KEY ?? "";
|
|
7
|
+
if (!ORAKEL_API_KEY) {
|
|
8
|
+
console.error("Error: ORAKEL_API_KEY environment variable is required.\n\n" +
|
|
9
|
+
"Request access at https://orakel.heiberg.co\n\n" +
|
|
10
|
+
"Then configure in Claude Code (~/.claude/settings.json):\n\n" +
|
|
11
|
+
' "mcpServers": {\n' +
|
|
12
|
+
' "orakel": {\n' +
|
|
13
|
+
' "command": "npx",\n' +
|
|
14
|
+
' "args": ["-y", "@heibergindustries/orakel-mcp"],\n' +
|
|
15
|
+
' "env": { "ORAKEL_API_KEY": "orakel_YOUR_KEY" }\n' +
|
|
16
|
+
' }\n' +
|
|
17
|
+
' }\n');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
async function orakelFetch(path, options) {
|
|
21
|
+
const url = `${ORAKEL_URL}${path}`;
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
...options,
|
|
24
|
+
headers: {
|
|
25
|
+
Authorization: `Bearer ${ORAKEL_API_KEY}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
...options?.headers,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
const text = await response.text();
|
|
32
|
+
throw new Error(`Orakel API error ${response.status}: ${text}`);
|
|
33
|
+
}
|
|
34
|
+
return response.json();
|
|
35
|
+
}
|
|
36
|
+
const server = new McpServer({
|
|
37
|
+
name: "orakel",
|
|
38
|
+
version: "1.0.0",
|
|
39
|
+
});
|
|
40
|
+
// ─── lookup_company ───────────────────────────────────────────
|
|
41
|
+
server.tool("lookup_company", "Look up a Norwegian company by organization number. Returns full details including financials, roles, and sub-units.", { orgNumber: z.string().describe("9-digit Norwegian organization number") }, async ({ orgNumber }) => {
|
|
42
|
+
const company = await orakelFetch(`/api/companies/${orgNumber}`);
|
|
43
|
+
return { content: [{ type: "text", text: JSON.stringify(company, null, 2) }] };
|
|
44
|
+
});
|
|
45
|
+
// ─── search_companies ─────────────────────────────────────────
|
|
46
|
+
server.tool("search_companies", "Search Norwegian companies by name, industry (NACE code), county, municipality, employee count, revenue, and more. Returns paginated results.", {
|
|
47
|
+
query: z.string().optional().describe("Search by company name"),
|
|
48
|
+
nace: z.string().optional().describe("NACE industry code prefix (e.g. '56.3' for bars/restaurants)"),
|
|
49
|
+
orgForm: z.string().optional().describe("Organization form code (AS, ASA, ENK, etc.)"),
|
|
50
|
+
county: z.string().optional().describe("2-digit county code (e.g. '39' for Vestfold, '03' for Oslo). Use list_municipalities to find codes."),
|
|
51
|
+
municipality: z.string().optional().describe("4-digit municipality number (e.g. '0301' for Oslo, '3905' for Tønsberg)"),
|
|
52
|
+
minEmployees: z.number().optional().describe("Minimum employee count"),
|
|
53
|
+
maxEmployees: z.number().optional().describe("Maximum employee count"),
|
|
54
|
+
minRevenue: z.number().optional().describe("Minimum revenue (NOK)"),
|
|
55
|
+
maxRevenue: z.number().optional().describe("Maximum revenue (NOK)"),
|
|
56
|
+
isBankrupt: z.boolean().optional().describe("Filter by bankruptcy status"),
|
|
57
|
+
isInGroup: z.boolean().optional().describe("Filter by whether company is part of a group"),
|
|
58
|
+
hasParent: z.boolean().optional().describe("Filter by whether company has a parent organization"),
|
|
59
|
+
hasTechnology: z.string().optional().describe("Filter by detected technology (e.g. 'WordPress', 'Shopify', 'HubSpot')"),
|
|
60
|
+
hasAnyAdPixel: z.boolean().optional().describe("Filter by companies running digital ads (Meta Pixel, Google Ads, etc.)"),
|
|
61
|
+
sort: z.string().optional().describe("Sort by: name, employeeCount, foundingDate, updatedAt"),
|
|
62
|
+
order: z.string().optional().describe("Sort order: asc or desc"),
|
|
63
|
+
limit: z.number().optional().describe("Max results (default 20, max 100)"),
|
|
64
|
+
}, async (params) => {
|
|
65
|
+
const searchParams = new URLSearchParams();
|
|
66
|
+
if (params.query)
|
|
67
|
+
searchParams.set("q", params.query);
|
|
68
|
+
if (params.nace)
|
|
69
|
+
searchParams.set("nace", params.nace);
|
|
70
|
+
if (params.orgForm)
|
|
71
|
+
searchParams.set("orgForm", params.orgForm);
|
|
72
|
+
if (params.county)
|
|
73
|
+
searchParams.set("county", params.county);
|
|
74
|
+
if (params.municipality)
|
|
75
|
+
searchParams.set("municipality", params.municipality);
|
|
76
|
+
if (params.minEmployees)
|
|
77
|
+
searchParams.set("minEmployees", String(params.minEmployees));
|
|
78
|
+
if (params.maxEmployees)
|
|
79
|
+
searchParams.set("maxEmployees", String(params.maxEmployees));
|
|
80
|
+
if (params.minRevenue)
|
|
81
|
+
searchParams.set("minRevenue", String(params.minRevenue));
|
|
82
|
+
if (params.maxRevenue)
|
|
83
|
+
searchParams.set("maxRevenue", String(params.maxRevenue));
|
|
84
|
+
if (params.isBankrupt !== undefined)
|
|
85
|
+
searchParams.set("isBankrupt", String(params.isBankrupt));
|
|
86
|
+
if (params.isInGroup !== undefined)
|
|
87
|
+
searchParams.set("isInGroup", String(params.isInGroup));
|
|
88
|
+
if (params.hasParent !== undefined)
|
|
89
|
+
searchParams.set("hasParent", String(params.hasParent));
|
|
90
|
+
if (params.hasTechnology)
|
|
91
|
+
searchParams.set("hasTechnology", params.hasTechnology);
|
|
92
|
+
if (params.hasAnyAdPixel !== undefined)
|
|
93
|
+
searchParams.set("hasAnyAdPixel", String(params.hasAnyAdPixel));
|
|
94
|
+
if (params.sort)
|
|
95
|
+
searchParams.set("sort", params.sort);
|
|
96
|
+
if (params.order)
|
|
97
|
+
searchParams.set("order", params.order);
|
|
98
|
+
if (params.limit)
|
|
99
|
+
searchParams.set("limit", String(params.limit));
|
|
100
|
+
const result = await orakelFetch(`/api/companies?${searchParams}`);
|
|
101
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
102
|
+
});
|
|
103
|
+
// ─── get_financials ───────────────────────────────────────────
|
|
104
|
+
server.tool("get_financials", "Get financial data (revenue, profit, assets, equity, debt) for a Norwegian company.", { orgNumber: z.string().describe("9-digit Norwegian organization number") }, async ({ orgNumber }) => {
|
|
105
|
+
const company = await orakelFetch(`/api/companies/${orgNumber}`);
|
|
106
|
+
const financials = company.financials ?? [];
|
|
107
|
+
return { content: [{ type: "text", text: JSON.stringify(financials, null, 2) }] };
|
|
108
|
+
});
|
|
109
|
+
// ─── find_prospects ───────────────────────────────────────────
|
|
110
|
+
server.tool("find_prospects", "Find potential business prospects by industry, location, size, revenue, and technology. Returns a formatted list with financials and tech stack.", {
|
|
111
|
+
nace: z.string().optional().describe("NACE industry code prefix"),
|
|
112
|
+
county: z.string().optional().describe("2-digit county code (e.g. '39' for Vestfold). Use list_municipalities to find codes."),
|
|
113
|
+
municipality: z.string().optional().describe("4-digit municipality number"),
|
|
114
|
+
minEmployees: z.number().optional().describe("Minimum employees"),
|
|
115
|
+
maxEmployees: z.number().optional().describe("Maximum employees"),
|
|
116
|
+
minRevenue: z.number().optional().describe("Minimum revenue (NOK)"),
|
|
117
|
+
hasTechnology: z.string().optional().describe("Filter by detected technology (e.g. 'WordPress', 'Shopify')"),
|
|
118
|
+
hasAnyAdPixel: z.boolean().optional().describe("Only companies running digital ads"),
|
|
119
|
+
limit: z.number().optional().describe("Max results (default 20)"),
|
|
120
|
+
}, async (params) => {
|
|
121
|
+
const searchParams = new URLSearchParams();
|
|
122
|
+
if (params.nace)
|
|
123
|
+
searchParams.set("nace", params.nace);
|
|
124
|
+
if (params.county)
|
|
125
|
+
searchParams.set("county", params.county);
|
|
126
|
+
if (params.municipality)
|
|
127
|
+
searchParams.set("municipality", params.municipality);
|
|
128
|
+
if (params.minEmployees)
|
|
129
|
+
searchParams.set("minEmployees", String(params.minEmployees));
|
|
130
|
+
if (params.maxEmployees)
|
|
131
|
+
searchParams.set("maxEmployees", String(params.maxEmployees));
|
|
132
|
+
if (params.minRevenue)
|
|
133
|
+
searchParams.set("minRevenue", String(params.minRevenue));
|
|
134
|
+
if (params.hasTechnology)
|
|
135
|
+
searchParams.set("hasTechnology", params.hasTechnology);
|
|
136
|
+
if (params.hasAnyAdPixel !== undefined)
|
|
137
|
+
searchParams.set("hasAnyAdPixel", String(params.hasAnyAdPixel));
|
|
138
|
+
searchParams.set("limit", String(params.limit ?? 20));
|
|
139
|
+
searchParams.set("sort", "employeeCount");
|
|
140
|
+
searchParams.set("order", "desc");
|
|
141
|
+
const result = await orakelFetch(`/api/companies?${searchParams}`);
|
|
142
|
+
const companies = result.data ?? [];
|
|
143
|
+
const formatted = companies.map((c) => {
|
|
144
|
+
const financials = c.financials ?? [];
|
|
145
|
+
const latest = financials[0];
|
|
146
|
+
return {
|
|
147
|
+
orgNumber: c.orgNumber,
|
|
148
|
+
name: c.name,
|
|
149
|
+
employees: c.employeeCount,
|
|
150
|
+
city: c.businessAddressCity,
|
|
151
|
+
nace: c.naceCode1,
|
|
152
|
+
revenue: latest?.revenue,
|
|
153
|
+
netResult: latest?.netResult,
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
return { content: [{ type: "text", text: JSON.stringify(formatted, null, 2) }] };
|
|
157
|
+
});
|
|
158
|
+
// ─── list_municipalities ─────────────────────────────────────
|
|
159
|
+
server.tool("list_municipalities", "List Norwegian municipalities and counties with their codes. Use this to find the right county or municipality code for search queries. For example, find that Vestfold = county code '39', or that Tønsberg = municipality '3905'.", {
|
|
160
|
+
county: z.string().optional().describe("2-digit county code to filter by (e.g. '39' for Vestfold)"),
|
|
161
|
+
}, async (params) => {
|
|
162
|
+
const searchParams = new URLSearchParams();
|
|
163
|
+
if (params.county)
|
|
164
|
+
searchParams.set("county", params.county);
|
|
165
|
+
const result = await orakelFetch(`/api/municipalities?${searchParams}`);
|
|
166
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
167
|
+
});
|
|
168
|
+
// ─── push_to_destination ──────────────────────────────────────
|
|
169
|
+
server.tool("push_to_destination", "Push company data to a configured destination (Attio CRM, webhook, etc.).", {
|
|
170
|
+
destinationName: z.string().describe("Name of the destination (e.g. 'zero7-attio')"),
|
|
171
|
+
orgNumbers: z.array(z.string()).describe("Array of 9-digit org numbers to push"),
|
|
172
|
+
}, async ({ destinationName, orgNumbers }) => {
|
|
173
|
+
const result = await orakelFetch(`/api/push/${destinationName}`, {
|
|
174
|
+
method: "POST",
|
|
175
|
+
body: JSON.stringify({ orgNumbers }),
|
|
176
|
+
});
|
|
177
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
178
|
+
});
|
|
179
|
+
// ─── list_destinations ────────────────────────────────────────
|
|
180
|
+
server.tool("list_destinations", "List all configured push destinations (Attio, webhooks, etc.).", {}, async () => {
|
|
181
|
+
const destinations = await orakelFetch("/api/destinations");
|
|
182
|
+
return { content: [{ type: "text", text: JSON.stringify(destinations, null, 2) }] };
|
|
183
|
+
});
|
|
184
|
+
// ─── manage_destination ───────────────────────────────────────
|
|
185
|
+
server.tool("manage_destination", "Create, update, or delete a push destination.", {
|
|
186
|
+
action: z.enum(["create", "update", "delete"]).describe("Action to perform"),
|
|
187
|
+
name: z.string().describe("Destination name"),
|
|
188
|
+
type: z.string().optional().describe("Destination type (attio, webhook)"),
|
|
189
|
+
config: z.record(z.string(), z.unknown()).optional().describe("Configuration object"),
|
|
190
|
+
isActive: z.boolean().optional().describe("Whether the destination is active"),
|
|
191
|
+
}, async ({ action, name, type, config, isActive }) => {
|
|
192
|
+
let result;
|
|
193
|
+
switch (action) {
|
|
194
|
+
case "create":
|
|
195
|
+
result = await orakelFetch("/api/destinations", {
|
|
196
|
+
method: "POST",
|
|
197
|
+
body: JSON.stringify({ name, type, config: config ?? {} }),
|
|
198
|
+
});
|
|
199
|
+
break;
|
|
200
|
+
case "update":
|
|
201
|
+
result = await orakelFetch(`/api/destinations/${name}`, {
|
|
202
|
+
method: "PUT",
|
|
203
|
+
body: JSON.stringify({ type, config, isActive }),
|
|
204
|
+
});
|
|
205
|
+
break;
|
|
206
|
+
case "delete":
|
|
207
|
+
await orakelFetch(`/api/destinations/${name}`, { method: "DELETE" });
|
|
208
|
+
result = { deleted: name };
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
212
|
+
});
|
|
213
|
+
// ─── search_inspections ──────────────────────────────────────
|
|
214
|
+
server.tool("search_inspections", "Search Mattilsynet food safety inspections (smilefjes). Filter by org number, rating (0=best, 3=worst), postal code, or date range.", {
|
|
215
|
+
orgNumber: z.string().optional().describe("Filter by 9-digit org number"),
|
|
216
|
+
minRating: z.number().optional().describe("Minimum overall rating (0=smiling, 3=very sad)"),
|
|
217
|
+
maxRating: z.number().optional().describe("Maximum overall rating"),
|
|
218
|
+
postnr: z.string().optional().describe("Postal code filter"),
|
|
219
|
+
fromDate: z.string().optional().describe("Inspections from this date (ISO format)"),
|
|
220
|
+
toDate: z.string().optional().describe("Inspections up to this date (ISO format)"),
|
|
221
|
+
limit: z.number().optional().describe("Max results (default 20)"),
|
|
222
|
+
}, async (params) => {
|
|
223
|
+
const searchParams = new URLSearchParams();
|
|
224
|
+
if (params.orgNumber)
|
|
225
|
+
searchParams.set("orgNumber", params.orgNumber);
|
|
226
|
+
if (params.minRating !== undefined)
|
|
227
|
+
searchParams.set("minRating", String(params.minRating));
|
|
228
|
+
if (params.maxRating !== undefined)
|
|
229
|
+
searchParams.set("maxRating", String(params.maxRating));
|
|
230
|
+
if (params.postnr)
|
|
231
|
+
searchParams.set("postnr", params.postnr);
|
|
232
|
+
if (params.fromDate)
|
|
233
|
+
searchParams.set("fromDate", params.fromDate);
|
|
234
|
+
if (params.toDate)
|
|
235
|
+
searchParams.set("toDate", params.toDate);
|
|
236
|
+
if (params.limit)
|
|
237
|
+
searchParams.set("limit", String(params.limit));
|
|
238
|
+
const result = await orakelFetch(`/api/inspections?${searchParams}`);
|
|
239
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
240
|
+
});
|
|
241
|
+
// ─── search_licenses ─────────────────────────────────────────
|
|
242
|
+
server.tool("search_licenses", "Search Norwegian alcohol and tobacco licenses (TBR). Filter by org number, license type, municipality, or active status.", {
|
|
243
|
+
orgNumber: z.string().optional().describe("Filter by org number"),
|
|
244
|
+
registerType: z.string().optional().describe("Alkoholsbevilling or Tobakksbevilling"),
|
|
245
|
+
licenseTypeCode: z.string().optional().describe("01SKJE (skjenke), 11ENGR (engros), 18TBIM (import), 19TBEK (export)"),
|
|
246
|
+
municipalityNo: z.string().optional().describe("Municipality number (e.g. '0301' for Oslo)"),
|
|
247
|
+
activeOnly: z.boolean().optional().describe("Only show currently valid licenses"),
|
|
248
|
+
limit: z.number().optional().describe("Max results (default 20)"),
|
|
249
|
+
}, async (params) => {
|
|
250
|
+
const searchParams = new URLSearchParams();
|
|
251
|
+
if (params.orgNumber)
|
|
252
|
+
searchParams.set("orgNumber", params.orgNumber);
|
|
253
|
+
if (params.registerType)
|
|
254
|
+
searchParams.set("registerType", params.registerType);
|
|
255
|
+
if (params.licenseTypeCode)
|
|
256
|
+
searchParams.set("licenseTypeCode", params.licenseTypeCode);
|
|
257
|
+
if (params.municipalityNo)
|
|
258
|
+
searchParams.set("municipalityNo", params.municipalityNo);
|
|
259
|
+
if (params.activeOnly !== undefined)
|
|
260
|
+
searchParams.set("activeOnly", String(params.activeOnly));
|
|
261
|
+
if (params.limit)
|
|
262
|
+
searchParams.set("limit", String(params.limit));
|
|
263
|
+
const result = await orakelFetch(`/api/licenses?${searchParams}`);
|
|
264
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
265
|
+
});
|
|
266
|
+
// ─── search_procurement ──────────────────────────────────────
|
|
267
|
+
server.tool("search_procurement", "Search Norwegian public procurement notices (Doffin). Filter by CPV code, contracting authority, value, status, or deadline.", {
|
|
268
|
+
query: z.string().optional().describe("Search in title"),
|
|
269
|
+
orgNumber: z.string().optional().describe("Contracting authority org number"),
|
|
270
|
+
cpvCode: z.string().optional().describe("CPV code (e.g. '72' for IT services)"),
|
|
271
|
+
status: z.string().optional().describe("Notice status: ACTIVE, EXPIRED"),
|
|
272
|
+
noticeType: z.string().optional().describe("Notice type: ANNOUNCEMENT_OF_COMPETITION, etc."),
|
|
273
|
+
minValue: z.number().optional().describe("Minimum estimated value (NOK)"),
|
|
274
|
+
deadlineAfter: z.string().optional().describe("Only notices with deadline after this date (ISO)"),
|
|
275
|
+
limit: z.number().optional().describe("Max results (default 20)"),
|
|
276
|
+
}, async (params) => {
|
|
277
|
+
const searchParams = new URLSearchParams();
|
|
278
|
+
if (params.query)
|
|
279
|
+
searchParams.set("q", params.query);
|
|
280
|
+
if (params.orgNumber)
|
|
281
|
+
searchParams.set("orgNumber", params.orgNumber);
|
|
282
|
+
if (params.cpvCode)
|
|
283
|
+
searchParams.set("cpvCode", params.cpvCode);
|
|
284
|
+
if (params.status)
|
|
285
|
+
searchParams.set("status", params.status);
|
|
286
|
+
if (params.noticeType)
|
|
287
|
+
searchParams.set("noticeType", params.noticeType);
|
|
288
|
+
if (params.minValue)
|
|
289
|
+
searchParams.set("minValue", String(params.minValue));
|
|
290
|
+
if (params.deadlineAfter)
|
|
291
|
+
searchParams.set("deadlineAfter", params.deadlineAfter);
|
|
292
|
+
if (params.limit)
|
|
293
|
+
searchParams.set("limit", String(params.limit));
|
|
294
|
+
const result = await orakelFetch(`/api/procurement?${searchParams}`);
|
|
295
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
296
|
+
});
|
|
297
|
+
// ─── enrichment_status ────────────────────────────────────────
|
|
298
|
+
server.tool("enrichment_status", "View domain enrichment results. Filter by status (confirmed, ambiguous, rejected) to review pipeline output. Shows company name, discovered domains, social handles, and confidence scores.", {
|
|
299
|
+
status: z.enum(["confirmed", "ambiguous", "rejected", "pending"]).optional().describe("Filter by enrichment status. Omit to see all non-pending results."),
|
|
300
|
+
minConfidence: z.number().optional().describe("Minimum confidence score (0-100)"),
|
|
301
|
+
limit: z.number().optional().describe("Max results (default 50, max 200)"),
|
|
302
|
+
}, async (params) => {
|
|
303
|
+
const searchParams = new URLSearchParams();
|
|
304
|
+
if (params.status)
|
|
305
|
+
searchParams.set("status", params.status);
|
|
306
|
+
if (params.minConfidence)
|
|
307
|
+
searchParams.set("minConfidence", String(params.minConfidence));
|
|
308
|
+
if (params.limit)
|
|
309
|
+
searchParams.set("limit", String(params.limit));
|
|
310
|
+
const result = await orakelFetch(`/api/enrichment?${searchParams}`);
|
|
311
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
312
|
+
});
|
|
313
|
+
// ─── get_corporate_group ─────────────────────────────────────
|
|
314
|
+
server.tool("get_corporate_group", "Get the corporate group tree for a company. Shows parent company and all subsidiaries in a hierarchy.", { orgNumber: z.string().describe("9-digit Norwegian organization number") }, async ({ orgNumber }) => {
|
|
315
|
+
const result = await orakelFetch(`/api/companies/${orgNumber}/group`);
|
|
316
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
317
|
+
});
|
|
318
|
+
// ─── get_shareholders ────────────────────────────────────────
|
|
319
|
+
server.tool("get_shareholders", "Get shareholders of a Norwegian company from the shareholder register (Aksjonærregisteret). Shows ownership percentages per share class.", {
|
|
320
|
+
orgNumber: z.string().describe("9-digit Norwegian organization number"),
|
|
321
|
+
year: z.number().optional().describe("Filter by snapshot year (e.g. 2024)"),
|
|
322
|
+
includePersons: z.boolean().optional().describe("Include individual (non-corporate) shareholders. Default: false for privacy."),
|
|
323
|
+
}, async ({ orgNumber, year, includePersons }) => {
|
|
324
|
+
const params = new URLSearchParams();
|
|
325
|
+
if (year)
|
|
326
|
+
params.set("year", String(year));
|
|
327
|
+
if (includePersons)
|
|
328
|
+
params.set("includePersons", "true");
|
|
329
|
+
const result = await orakelFetch(`/api/companies/${orgNumber}/shareholders?${params}`);
|
|
330
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
331
|
+
});
|
|
332
|
+
// ─── get_ownership_network ───────────────────────────────────
|
|
333
|
+
server.tool("get_ownership_network", "Find all companies where a given company is a shareholder (reverse ownership lookup). Answer: 'What does company X own?'", {
|
|
334
|
+
shareholderOrgNumber: z.string().describe("Org number of the shareholder company"),
|
|
335
|
+
year: z.number().optional().describe("Filter by snapshot year"),
|
|
336
|
+
}, async ({ shareholderOrgNumber, year }) => {
|
|
337
|
+
const params = new URLSearchParams({ shareholderOrgNumber });
|
|
338
|
+
if (year)
|
|
339
|
+
params.set("year", String(year));
|
|
340
|
+
const result = await orakelFetch(`/api/shareholders/by-owner?${params}`);
|
|
341
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
342
|
+
});
|
|
343
|
+
// ─── check_health ─────────────────────────────────────────────
|
|
344
|
+
server.tool("check_health", "Check Orakel API health, database status, record counts, and sync status for all data sources.", {}, async () => {
|
|
345
|
+
const health = await orakelFetch("/api/health");
|
|
346
|
+
return { content: [{ type: "text", text: JSON.stringify(health, null, 2) }] };
|
|
347
|
+
});
|
|
348
|
+
// ─── Start server ─────────────────────────────────────────────
|
|
349
|
+
const transport = new StdioServerTransport();
|
|
350
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heibergindustries/orakel-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Orakel — Norwegian company data, financials, and prospecting tools for Claude Code, Claude Desktop & Gemini CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"orakel-mcp": "dist/server.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"skills"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && chmod +x dist/server.js",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
19
|
+
"zod": "^4.3.6"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/bendikheiberg/orakel",
|
|
31
|
+
"directory": "mcp"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"mcp",
|
|
35
|
+
"model-context-protocol",
|
|
36
|
+
"claude",
|
|
37
|
+
"gemini",
|
|
38
|
+
"norway",
|
|
39
|
+
"company-data",
|
|
40
|
+
"business-intelligence"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: company-deep-dive
|
|
3
|
+
description: >
|
|
4
|
+
Research a single Norwegian company in depth. Use when asked to look up,
|
|
5
|
+
analyze, or investigate a specific company.
|
|
6
|
+
allowed-tools: mcp__orakel__lookup_company, mcp__orakel__get_financials, mcp__orakel__get_corporate_group, mcp__orakel__get_shareholders, mcp__orakel__get_ownership_network, mcp__orakel__search_companies, mcp__orakel__search_inspections, mcp__orakel__search_licenses
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Company Deep Dive
|
|
10
|
+
|
|
11
|
+
Produce a comprehensive research report on a single Norwegian company.
|
|
12
|
+
|
|
13
|
+
## Step 1: Identify the Company
|
|
14
|
+
|
|
15
|
+
The user may provide:
|
|
16
|
+
|
|
17
|
+
- **Organization number** (9 digits like 999888777): Use `lookup_company` directly.
|
|
18
|
+
- **Company name**: Use `search_companies` with the `query` parameter to find it.
|
|
19
|
+
|
|
20
|
+
If a name search returns multiple results, present the top matches and ask the user to confirm:
|
|
21
|
+
|
|
22
|
+
> I found several companies matching that name:
|
|
23
|
+
> 1. Example AS (999888777) - Oslo, IT consulting, 45 employees
|
|
24
|
+
> 2. Example Norge AS (888777666) - Bergen, retail, 12 employees
|
|
25
|
+
>
|
|
26
|
+
> Which one did you mean?
|
|
27
|
+
|
|
28
|
+
## Step 2: Gather All Data
|
|
29
|
+
|
|
30
|
+
Once you have the org number, make these calls:
|
|
31
|
+
|
|
32
|
+
1. `lookup_company` with the org number — this returns the core company data, roles, financials, sub-units, technologies, and enrichment data.
|
|
33
|
+
2. `get_corporate_group` — to see parent company and subsidiaries.
|
|
34
|
+
3. `get_shareholders` — to see ownership structure. Start without `includePersons` for privacy. Mention that personal shareholders can be included if needed.
|
|
35
|
+
|
|
36
|
+
If the company's NACE code starts with 55 or 56 (hospitality/food service), also run:
|
|
37
|
+
4. `search_inspections` with the org number — Mattilsynet food safety ratings.
|
|
38
|
+
5. `search_licenses` with the org number — alcohol/tobacco licenses.
|
|
39
|
+
|
|
40
|
+
## Step 3: Produce the Report
|
|
41
|
+
|
|
42
|
+
Structure the report with these sections:
|
|
43
|
+
|
|
44
|
+
### Overview
|
|
45
|
+
- Full legal name and any brand names
|
|
46
|
+
- Organization number
|
|
47
|
+
- Organization form (AS, ASA, ENK, etc.)
|
|
48
|
+
- Founding date and age
|
|
49
|
+
- Registered address and business address
|
|
50
|
+
- NACE industry code with plain-language description
|
|
51
|
+
- Number of employees
|
|
52
|
+
- Website and social media (from enrichment data if available)
|
|
53
|
+
- Technologies detected (if any)
|
|
54
|
+
|
|
55
|
+
### Financial Performance
|
|
56
|
+
|
|
57
|
+
Present a multi-year table (most recent years first):
|
|
58
|
+
|
|
59
|
+
| Year | Revenue (MNOK) | Net Result (MNOK) | Profit Margin | Assets (MNOK) | Equity Ratio |
|
|
60
|
+
|------|---------------|-------------------|---------------|---------------|-------------|
|
|
61
|
+
| 2023 | 32.5 | 4.1 | 12.6% | 28.0 | 42% |
|
|
62
|
+
| 2022 | 28.9 | 3.2 | 11.1% | 24.5 | 38% |
|
|
63
|
+
|
|
64
|
+
Calculate and highlight:
|
|
65
|
+
- **Revenue trend:** Year-over-year growth percentage
|
|
66
|
+
- **Profit margin:** Net result / revenue (as percentage)
|
|
67
|
+
- **Equity ratio:** Equity / total assets (as percentage)
|
|
68
|
+
|
|
69
|
+
#### Interpreting the Numbers
|
|
70
|
+
|
|
71
|
+
Use these benchmarks to explain financial health in plain language:
|
|
72
|
+
|
|
73
|
+
| Metric | Healthy | Moderate | Risky |
|
|
74
|
+
|--------|---------|----------|-------|
|
|
75
|
+
| Profit margin | > 10% | 3-10% | < 3% or negative |
|
|
76
|
+
| Equity ratio | > 30% | 20-30% | < 20% |
|
|
77
|
+
| Revenue growth | > 10% | 0-10% | Declining |
|
|
78
|
+
| Debt-to-equity | < 2x | 2-4x | > 4x |
|
|
79
|
+
|
|
80
|
+
Examples of plain-language interpretation:
|
|
81
|
+
- "Revenue has grown 12% year-over-year, showing healthy expansion."
|
|
82
|
+
- "The equity ratio of 18% is below the 20% threshold, which means the company is heavily leveraged."
|
|
83
|
+
- "Profit margins have been declining for three consecutive years, from 15% to 8%."
|
|
84
|
+
|
|
85
|
+
### Key People
|
|
86
|
+
|
|
87
|
+
List board members, CEO, and other roles from the company data. Format as:
|
|
88
|
+
|
|
89
|
+
- **Board Chair:** Name (since year)
|
|
90
|
+
- **CEO / Daglig leder:** Name (since year)
|
|
91
|
+
- **Board Members:** Name 1, Name 2, Name 3
|
|
92
|
+
|
|
93
|
+
### Ownership Structure
|
|
94
|
+
|
|
95
|
+
From `get_shareholders`:
|
|
96
|
+
- List corporate shareholders with ownership percentages
|
|
97
|
+
- Note majority shareholders (> 50%) and significant holders (> 10%)
|
|
98
|
+
- If requested, include personal shareholders
|
|
99
|
+
|
|
100
|
+
### Corporate Group
|
|
101
|
+
|
|
102
|
+
From `get_corporate_group`:
|
|
103
|
+
- Show parent company (if any)
|
|
104
|
+
- List subsidiaries (if any)
|
|
105
|
+
- Note the company's position in the hierarchy
|
|
106
|
+
|
|
107
|
+
### Food Safety and Licenses (hospitality only)
|
|
108
|
+
|
|
109
|
+
If the company is in NACE 55-56:
|
|
110
|
+
|
|
111
|
+
**Inspections (Smilefjes):**
|
|
112
|
+
- Most recent inspection date and rating
|
|
113
|
+
- Rating scale: 0 = smiling face (best), 1 = neutral, 2 = sad, 3 = very sad (worst)
|
|
114
|
+
- Any recent downgrades or improvements
|
|
115
|
+
|
|
116
|
+
**Licenses:**
|
|
117
|
+
- Active alcohol/tobacco licenses with type and validity period
|
|
118
|
+
- License types: 01SKJE = serving license, 11ENGR = wholesale, 18TBIM = import, 19TBEK = export
|
|
119
|
+
|
|
120
|
+
## Step 4: Closing Summary
|
|
121
|
+
|
|
122
|
+
End the report with:
|
|
123
|
+
- A one-paragraph plain-language summary of the company's health and position
|
|
124
|
+
- The org number for reference: "Org number: 999888777"
|
|
125
|
+
- Offer next steps: "Would you like me to check what this company owns, push it to your CRM, or compare it with competitors?"
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: market-scan
|
|
3
|
+
description: >
|
|
4
|
+
Analyze a Norwegian market segment — size, top players, technology landscape,
|
|
5
|
+
procurement opportunities, regulatory status. Use for market research or
|
|
6
|
+
proposal preparation.
|
|
7
|
+
allowed-tools: mcp__orakel__search_companies, mcp__orakel__find_prospects, mcp__orakel__search_procurement, mcp__orakel__search_licenses, mcp__orakel__search_inspections, mcp__orakel__list_municipalities
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Market Scan
|
|
11
|
+
|
|
12
|
+
Produce a market analysis of a Norwegian industry segment, suitable for proposals, strategy documents, or competitive intelligence.
|
|
13
|
+
|
|
14
|
+
## Step 1: Define the Market Segment
|
|
15
|
+
|
|
16
|
+
Map the user's description to concrete filters. Ask clarifying questions if needed:
|
|
17
|
+
|
|
18
|
+
- **What industry?** Map to NACE code(s).
|
|
19
|
+
- **What geography?** Nationwide, specific county, or specific municipality.
|
|
20
|
+
- **What size range?** All companies, or only above a certain threshold.
|
|
21
|
+
|
|
22
|
+
### NACE Industry Codes
|
|
23
|
+
|
|
24
|
+
| Code | Industry |
|
|
25
|
+
|------|----------|
|
|
26
|
+
| 01-03 | Agriculture, forestry, fishing |
|
|
27
|
+
| 10-12 | Food, beverage, and tobacco manufacturing |
|
|
28
|
+
| 41-43 | Construction (buildings, civil engineering, specialized) |
|
|
29
|
+
| 46 | Wholesale trade |
|
|
30
|
+
| 47 | Retail trade |
|
|
31
|
+
| 50 | Shipping / water transport |
|
|
32
|
+
| 55 | Hotels and accommodation |
|
|
33
|
+
| 56.1 | Restaurants and cafes |
|
|
34
|
+
| 56.3 | Bars and pubs |
|
|
35
|
+
| 62.01 | Software development |
|
|
36
|
+
| 62.02 | IT consulting |
|
|
37
|
+
| 62.09 | Other IT services |
|
|
38
|
+
| 64 | Financial services |
|
|
39
|
+
| 69.1 | Accounting and auditing |
|
|
40
|
+
| 69.2 | Legal services |
|
|
41
|
+
| 70.22 | Management consulting |
|
|
42
|
+
| 73.1 | Advertising agencies |
|
|
43
|
+
| 85 | Education |
|
|
44
|
+
| 86 | Health services |
|
|
45
|
+
|
|
46
|
+
### County Codes
|
|
47
|
+
|
|
48
|
+
| Code | County |
|
|
49
|
+
|------|--------|
|
|
50
|
+
| 03 | Oslo |
|
|
51
|
+
| 11 | Rogaland |
|
|
52
|
+
| 15 | More og Romsdal |
|
|
53
|
+
| 18 | Nordland |
|
|
54
|
+
| 30 | Viken |
|
|
55
|
+
| 34 | Innlandet |
|
|
56
|
+
| 38 | Vestfold og Telemark |
|
|
57
|
+
| 42 | Agder |
|
|
58
|
+
| 46 | Vestland |
|
|
59
|
+
| 50 | Trondelag |
|
|
60
|
+
| 54 | Troms og Finnmark |
|
|
61
|
+
|
|
62
|
+
Use `list_municipalities` if the user names a specific city to find the municipality code.
|
|
63
|
+
|
|
64
|
+
## Step 2: Market Overview
|
|
65
|
+
|
|
66
|
+
Run `search_companies` with the NACE and region filters. Set `limit: 100` and `sort: employeeCount`, `order: desc` to get the biggest players first.
|
|
67
|
+
|
|
68
|
+
Produce these metrics from the results:
|
|
69
|
+
|
|
70
|
+
### Segment Size
|
|
71
|
+
- Total number of companies matching the criteria
|
|
72
|
+
- Total employees across the segment (sum of employeeCount)
|
|
73
|
+
- Estimated total revenue (sum of latest revenue from financials, where available)
|
|
74
|
+
|
|
75
|
+
### Size Distribution
|
|
76
|
+
Categorize companies by employee count:
|
|
77
|
+
|
|
78
|
+
| Size Class | Employees | Count |
|
|
79
|
+
|------------|-----------|-------|
|
|
80
|
+
| Micro | 1-4 | |
|
|
81
|
+
| Small | 5-19 | |
|
|
82
|
+
| Medium | 20-99 | |
|
|
83
|
+
| Large | 100-249 | |
|
|
84
|
+
| Enterprise | 250+ | |
|
|
85
|
+
|
|
86
|
+
### Top 10 by Revenue
|
|
87
|
+
|
|
88
|
+
| # | Company | Org Number | Location | Employees | Revenue (MNOK) |
|
|
89
|
+
|---|---------|-----------|----------|-----------|----------------|
|
|
90
|
+
| 1 | ... | ... | ... | ... | ... |
|
|
91
|
+
|
|
92
|
+
Format revenue in millions NOK (MNOK), rounded to one decimal.
|
|
93
|
+
|
|
94
|
+
## Step 3: Technology Landscape
|
|
95
|
+
|
|
96
|
+
If tech stack data is available, summarize:
|
|
97
|
+
- Most common technologies detected (WordPress, Shopify, HubSpot, etc.)
|
|
98
|
+
- Percentage of companies running digital ads (hasAnyAdPixel)
|
|
99
|
+
- This reveals the digital maturity of the segment.
|
|
100
|
+
|
|
101
|
+
## Step 4: Procurement Opportunities
|
|
102
|
+
|
|
103
|
+
Use `search_procurement` to find active public tenders relevant to the sector.
|
|
104
|
+
|
|
105
|
+
Map NACE codes to CPV codes for procurement search:
|
|
106
|
+
|
|
107
|
+
| NACE sector | CPV code prefix | Description |
|
|
108
|
+
|-------------|----------------|-------------|
|
|
109
|
+
| 62 (IT) | 72 | IT services |
|
|
110
|
+
| 41-43 (Construction) | 45 | Construction work |
|
|
111
|
+
| 85 (Education) | 80 | Education services |
|
|
112
|
+
| 86 (Health) | 85 | Health services |
|
|
113
|
+
| 73 (Advertising) | 79 | Business services |
|
|
114
|
+
| 55-56 (Hospitality) | 55 | Hotel and restaurant services |
|
|
115
|
+
|
|
116
|
+
Filter with `status: "ACTIVE"` and `deadlineAfter` set to today's date to show only open tenders.
|
|
117
|
+
|
|
118
|
+
Present as:
|
|
119
|
+
|
|
120
|
+
### Active Tenders
|
|
121
|
+
| Title | Contracting Authority | Deadline | Est. Value (MNOK) |
|
|
122
|
+
|-------|-----------------------|----------|-------------------|
|
|
123
|
+
|
|
124
|
+
If no active tenders are found, say so explicitly — that's useful information too.
|
|
125
|
+
|
|
126
|
+
## Step 5: Regulatory Landscape (Hospitality and Food Sectors Only)
|
|
127
|
+
|
|
128
|
+
For NACE 55-56 segments, add:
|
|
129
|
+
|
|
130
|
+
### Food Safety Overview
|
|
131
|
+
Use `search_inspections` to get recent inspections in the region. Summarize:
|
|
132
|
+
- Distribution of ratings (how many 0/1/2/3)
|
|
133
|
+
- Average rating for the segment
|
|
134
|
+
- Rating scale: 0 = best (smiling face), 3 = worst (very sad face)
|
|
135
|
+
|
|
136
|
+
### License Landscape
|
|
137
|
+
Use `search_licenses` to check the segment. Summarize:
|
|
138
|
+
- Number of active alcohol serving licenses (01SKJE)
|
|
139
|
+
- Number of wholesale licenses (11ENGR)
|
|
140
|
+
- Import/export licenses (18TBIM / 19TBEK)
|
|
141
|
+
|
|
142
|
+
## Step 6: Format for Output
|
|
143
|
+
|
|
144
|
+
Structure the final report under clear headings. Use tables wherever possible. The output should be ready to copy-paste into a proposal or strategy document.
|
|
145
|
+
|
|
146
|
+
### Suggested Report Structure
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
# Market Analysis: [Industry] in [Region]
|
|
150
|
+
|
|
151
|
+
## Executive Summary
|
|
152
|
+
[2-3 sentence overview of the market]
|
|
153
|
+
|
|
154
|
+
## Market Size
|
|
155
|
+
[Segment size metrics and size distribution table]
|
|
156
|
+
|
|
157
|
+
## Top Players
|
|
158
|
+
[Top 10 table by revenue]
|
|
159
|
+
|
|
160
|
+
## Technology Adoption
|
|
161
|
+
[Tech landscape summary]
|
|
162
|
+
|
|
163
|
+
## Public Procurement
|
|
164
|
+
[Active tenders or note that none exist]
|
|
165
|
+
|
|
166
|
+
## Regulatory Landscape (if applicable)
|
|
167
|
+
[Inspection and license summary]
|
|
168
|
+
|
|
169
|
+
## Key Takeaways
|
|
170
|
+
[3-5 bullet points summarizing the most important findings]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
If the user mentions this is for a specific proposal or client, adapt the framing accordingly.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ownership-map
|
|
3
|
+
description: >
|
|
4
|
+
Trace ownership networks and corporate hierarchies for Norwegian companies.
|
|
5
|
+
Use for due diligence, competitive intelligence, or compliance research.
|
|
6
|
+
allowed-tools: mcp__orakel__lookup_company, mcp__orakel__get_shareholders, mcp__orakel__get_ownership_network, mcp__orakel__get_corporate_group
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Ownership Map
|
|
10
|
+
|
|
11
|
+
Trace ownership structures, corporate hierarchies, and shareholder networks for Norwegian companies.
|
|
12
|
+
|
|
13
|
+
## Step 1: Identify the Target Company
|
|
14
|
+
|
|
15
|
+
The user may provide:
|
|
16
|
+
|
|
17
|
+
- **Organization number** (9 digits): Use `lookup_company` directly.
|
|
18
|
+
- **Company name**: Use `lookup_company` to search. If ambiguous, list the top matches and ask the user to confirm.
|
|
19
|
+
|
|
20
|
+
Get the basic company info first so you can reference the name and org number throughout the analysis.
|
|
21
|
+
|
|
22
|
+
## Step 2: Gather Ownership Data
|
|
23
|
+
|
|
24
|
+
Make these calls:
|
|
25
|
+
|
|
26
|
+
1. **`get_shareholders`** with the org number — who owns this company?
|
|
27
|
+
- Start with `includePersons: false` for privacy (only shows corporate shareholders).
|
|
28
|
+
- Mention that personal shareholders can be revealed if the user requests it.
|
|
29
|
+
|
|
30
|
+
2. **`get_ownership_network`** with the org number as `shareholderOrgNumber` — what does this company own?
|
|
31
|
+
- This is the reverse lookup: shows companies where the target is a shareholder.
|
|
32
|
+
|
|
33
|
+
3. **`get_corporate_group`** with the org number — formal corporate hierarchy.
|
|
34
|
+
- Shows the parent company and all subsidiaries registered in Brreg.
|
|
35
|
+
- This is the legal group structure, which may differ from the shareholder-based ownership.
|
|
36
|
+
|
|
37
|
+
## Step 3: Build the Ownership Picture
|
|
38
|
+
|
|
39
|
+
### 3a: Upward Ownership (Who Owns This Company?)
|
|
40
|
+
|
|
41
|
+
From `get_shareholders`, list all shareholders with their ownership percentage:
|
|
42
|
+
|
|
43
|
+
**Shareholders of [Company Name] (org: 999888777):**
|
|
44
|
+
|
|
45
|
+
| Shareholder | Org Number | Ownership % | Type |
|
|
46
|
+
|-------------|-----------|-------------|------|
|
|
47
|
+
| Parent Holdings AS | 888777666 | 67.0% | Majority |
|
|
48
|
+
| Investment Fund AS | 777666555 | 22.5% | Significant |
|
|
49
|
+
| Other shareholders | - | 10.5% | Minor |
|
|
50
|
+
|
|
51
|
+
Classification thresholds:
|
|
52
|
+
- **Majority:** > 50% ownership (controls the company)
|
|
53
|
+
- **Significant:** > 10% ownership (meaningful influence)
|
|
54
|
+
- **Minor:** < 10% ownership
|
|
55
|
+
|
|
56
|
+
### 3b: Downward Ownership (What Does This Company Own?)
|
|
57
|
+
|
|
58
|
+
From `get_ownership_network`, list all companies where the target is a shareholder:
|
|
59
|
+
|
|
60
|
+
**Companies owned by [Company Name]:**
|
|
61
|
+
|
|
62
|
+
| Company | Org Number | Ownership % | Employees | Revenue (MNOK) |
|
|
63
|
+
|---------|-----------|-------------|-----------|----------------|
|
|
64
|
+
| Subsidiary One AS | 666555444 | 100% | 25 | 18.5 |
|
|
65
|
+
| Joint Venture AS | 555444333 | 50% | 8 | 5.2 |
|
|
66
|
+
|
|
67
|
+
### 3c: Formal Corporate Group
|
|
68
|
+
|
|
69
|
+
From `get_corporate_group`, show the registered group hierarchy:
|
|
70
|
+
|
|
71
|
+
**Corporate group structure:**
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
[Ultimate Parent] Holding Group AS (111222333)
|
|
75
|
+
|
|
|
76
|
+
+-- [Parent] Parent Holdings AS (888777666)
|
|
77
|
+
| |
|
|
78
|
+
| +-- [Target] Company Name AS (999888777) <-- target company
|
|
79
|
+
| |
|
|
80
|
+
| +-- Sibling Company AS (444333222)
|
|
81
|
+
|
|
|
82
|
+
+-- Other Branch AS (333222111)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Note: The formal group structure (from Brreg) may differ from shareholder-based ownership. The group structure shows the registered parent-subsidiary relationships, while shareholders show the actual equity ownership.
|
|
86
|
+
|
|
87
|
+
## Step 4: Trace One Level Deeper (if warranted)
|
|
88
|
+
|
|
89
|
+
For majority shareholders and significant holdings, offer to trace one level deeper:
|
|
90
|
+
|
|
91
|
+
> The majority shareholder is Parent Holdings AS (888777666). Want me to check who owns that company too?
|
|
92
|
+
|
|
93
|
+
**Important: Limit recursion to 2 levels maximum.** Beyond that, the network becomes unwieldy and may trigger many API calls. If the user wants to go deeper, do it one step at a time.
|
|
94
|
+
|
|
95
|
+
For each additional level, use `get_shareholders` on the parent company.
|
|
96
|
+
|
|
97
|
+
## Step 5: Build a Text Tree Visualization
|
|
98
|
+
|
|
99
|
+
Combine all findings into a single ownership tree. Use indentation and lines to show relationships:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Ownership Network for Company Name AS (999888777)
|
|
103
|
+
==================================================
|
|
104
|
+
|
|
105
|
+
OWNERS (who owns this company):
|
|
106
|
+
[67.0%] Parent Holdings AS (888777666)
|
|
107
|
+
[100%] Ultimate Owner AS (111222333) <-- level 2
|
|
108
|
+
[22.5%] Investment Fund AS (777666555)
|
|
109
|
+
[10.5%] Other/personal shareholders
|
|
110
|
+
|
|
111
|
+
SUBSIDIARIES (what this company owns):
|
|
112
|
+
[100%] Subsidiary One AS (666555444) - 25 employees, 18.5 MNOK revenue
|
|
113
|
+
[ 50%] Joint Venture AS (555444333) - 8 employees, 5.2 MNOK revenue
|
|
114
|
+
|
|
115
|
+
CORPORATE GROUP (formal Brreg registration):
|
|
116
|
+
Ultimate Owner AS (111222333)
|
|
117
|
+
+-- Parent Holdings AS (888777666)
|
|
118
|
+
+-- ** Company Name AS (999888777) ** <-- target
|
|
119
|
+
+-- Sibling Company AS (444333222)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Step 6: Flag Notable Patterns
|
|
123
|
+
|
|
124
|
+
Highlight any of these patterns if found:
|
|
125
|
+
|
|
126
|
+
- **Circular ownership:** Company A owns B, which owns A (or through intermediaries). This is unusual and worth flagging.
|
|
127
|
+
- **Shell company indicators:** A shareholder company with zero employees and zero revenue that exists solely as a holding entity. Not necessarily suspicious, but worth noting.
|
|
128
|
+
- **Same person/entity appearing multiple times:** If the same shareholder appears in multiple positions across the network, highlight this — it may indicate concentrated control.
|
|
129
|
+
- **Mismatch between group structure and ownership:** If the Brreg group tree and shareholder data tell different stories, note the discrepancy.
|
|
130
|
+
- **Foreign ownership:** If a shareholder is not found in Norwegian registries, it may be a foreign entity.
|
|
131
|
+
- **Recent changes:** If shareholder data shows different ownership across years, mention the change.
|
|
132
|
+
|
|
133
|
+
## Step 7: Closing Summary
|
|
134
|
+
|
|
135
|
+
End with:
|
|
136
|
+
|
|
137
|
+
- A plain-language summary: "Company Name is majority-owned (67%) by Parent Holdings, which is in turn fully owned by Ultimate Owner. The company itself holds two subsidiaries."
|
|
138
|
+
- The target company's org number for reference.
|
|
139
|
+
- Offer next steps: "Want me to do a deep dive on any of these related companies, or check the financials across the group?"
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prospect
|
|
3
|
+
description: >
|
|
4
|
+
Find and qualify Norwegian business prospects by industry, location, and size.
|
|
5
|
+
Use when looking for potential clients, leads, or market opportunities.
|
|
6
|
+
allowed-tools: mcp__orakel__search_companies, mcp__orakel__find_prospects, mcp__orakel__list_municipalities, mcp__orakel__get_financials, mcp__orakel__push_to_destination, mcp__orakel__list_destinations
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Prospect Finder
|
|
10
|
+
|
|
11
|
+
Find and qualify Norwegian business prospects using Orakel's company database.
|
|
12
|
+
|
|
13
|
+
## Step 1: Understand What the User is Looking For
|
|
14
|
+
|
|
15
|
+
Before searching, clarify these dimensions. If the user is vague, ask structured questions:
|
|
16
|
+
|
|
17
|
+
- **Industry:** What type of business? (restaurants, IT, construction, retail, etc.)
|
|
18
|
+
- **Region:** Where? (specific city, county, or all of Norway)
|
|
19
|
+
- **Size:** How big? (number of employees, revenue range)
|
|
20
|
+
- **Other filters:** Technology stack, ad spend, part of a corporate group?
|
|
21
|
+
|
|
22
|
+
Example prompt if the user says "find me some prospects":
|
|
23
|
+
> I can search Norwegian companies by industry, location, and size. Could you tell me:
|
|
24
|
+
> 1. What industry are you targeting?
|
|
25
|
+
> 2. Any geographic preference? (city, county, or nationwide)
|
|
26
|
+
> 3. What company size range? (employees or revenue)
|
|
27
|
+
|
|
28
|
+
## Step 2: Map Request to Search Filters
|
|
29
|
+
|
|
30
|
+
Translate the user's plain-language request into API parameters using these reference tables.
|
|
31
|
+
|
|
32
|
+
### NACE Industry Codes (use as prefix with `nace` parameter)
|
|
33
|
+
|
|
34
|
+
| Code | Industry |
|
|
35
|
+
|------|----------|
|
|
36
|
+
| 01 | Agriculture, crop production |
|
|
37
|
+
| 02 | Forestry |
|
|
38
|
+
| 03 | Fishing and aquaculture |
|
|
39
|
+
| 10 | Food manufacturing |
|
|
40
|
+
| 11 | Beverage manufacturing |
|
|
41
|
+
| 12 | Tobacco manufacturing |
|
|
42
|
+
| 41 | Building construction |
|
|
43
|
+
| 42 | Civil engineering |
|
|
44
|
+
| 43 | Specialized construction |
|
|
45
|
+
| 46 | Wholesale trade |
|
|
46
|
+
| 47 | Retail trade |
|
|
47
|
+
| 50 | Shipping / water transport |
|
|
48
|
+
| 55 | Hotels and accommodation |
|
|
49
|
+
| 56.1 | Restaurants and cafes |
|
|
50
|
+
| 56.3 | Bars and pubs |
|
|
51
|
+
| 62.01 | Software development |
|
|
52
|
+
| 62.02 | IT consulting |
|
|
53
|
+
| 62.09 | Other IT services |
|
|
54
|
+
| 64 | Financial services |
|
|
55
|
+
| 69.1 | Accounting and auditing |
|
|
56
|
+
| 69.2 | Legal services |
|
|
57
|
+
| 70.22 | Management consulting |
|
|
58
|
+
| 73.1 | Advertising agencies |
|
|
59
|
+
| 85 | Education |
|
|
60
|
+
| 86 | Health services |
|
|
61
|
+
|
|
62
|
+
Use partial codes for broader searches: `56` matches all food/drink, `62` matches all IT, `41` to `43` matches all construction.
|
|
63
|
+
|
|
64
|
+
### County Codes (use with `county` parameter)
|
|
65
|
+
|
|
66
|
+
| Code | County |
|
|
67
|
+
|------|--------|
|
|
68
|
+
| 03 | Oslo |
|
|
69
|
+
| 11 | Rogaland |
|
|
70
|
+
| 15 | More og Romsdal |
|
|
71
|
+
| 18 | Nordland |
|
|
72
|
+
| 30 | Viken |
|
|
73
|
+
| 34 | Innlandet |
|
|
74
|
+
| 38 | Vestfold og Telemark |
|
|
75
|
+
| 42 | Agder |
|
|
76
|
+
| 46 | Vestland |
|
|
77
|
+
| 50 | Trondelag |
|
|
78
|
+
| 54 | Troms og Finnmark |
|
|
79
|
+
|
|
80
|
+
If the user names a city instead of a county, use `list_municipalities` to find the municipality code and use the `municipality` parameter instead.
|
|
81
|
+
|
|
82
|
+
### Organization Forms
|
|
83
|
+
|
|
84
|
+
| Code | Type |
|
|
85
|
+
|------|------|
|
|
86
|
+
| AS | Private limited company (aksjeselskap) — most common |
|
|
87
|
+
| ASA | Public limited company |
|
|
88
|
+
| ENK | Sole proprietorship |
|
|
89
|
+
| NUF | Norwegian branch of foreign company |
|
|
90
|
+
| SA | Cooperative |
|
|
91
|
+
|
|
92
|
+
## Step 3: Run the Search
|
|
93
|
+
|
|
94
|
+
Use `find_prospects` for prospect-oriented results (pre-sorted by size, includes financials).
|
|
95
|
+
Use `search_companies` for more flexible filtering (custom sort, more filter options).
|
|
96
|
+
|
|
97
|
+
Present results as a ranked summary table:
|
|
98
|
+
|
|
99
|
+
| # | Company | Org Number | Location | Employees | Revenue (MNOK) |
|
|
100
|
+
|---|---------|-----------|----------|-----------|----------------|
|
|
101
|
+
| 1 | Example AS | 999888777 | Oslo | 45 | 32.5 |
|
|
102
|
+
|
|
103
|
+
Format revenue in millions NOK (MNOK) for readability. Round to one decimal.
|
|
104
|
+
|
|
105
|
+
If no results are found:
|
|
106
|
+
- Try broadening the NACE code (use parent code like `56` instead of `56.1`)
|
|
107
|
+
- Try removing the county filter
|
|
108
|
+
- Try lowering the minimum employee/revenue threshold
|
|
109
|
+
- Suggest alternative NACE codes that might match what the user meant
|
|
110
|
+
|
|
111
|
+
## Step 4: Offer Detailed Financials
|
|
112
|
+
|
|
113
|
+
After presenting the summary, offer:
|
|
114
|
+
|
|
115
|
+
> Would you like me to pull detailed financials for any of these companies? I can show multi-year revenue, profit, and key ratios.
|
|
116
|
+
|
|
117
|
+
Use `get_financials` for selected companies. Present year-over-year trends:
|
|
118
|
+
|
|
119
|
+
- Revenue trend (growing, stable, declining)
|
|
120
|
+
- Profit margin (net result / revenue)
|
|
121
|
+
- Whether they appear financially healthy
|
|
122
|
+
|
|
123
|
+
### Financial Health Quick Guide
|
|
124
|
+
|
|
125
|
+
| Metric | Healthy | Caution | Warning |
|
|
126
|
+
|--------|---------|---------|---------|
|
|
127
|
+
| Profit margin | > 10% | 3-10% | < 3% or negative |
|
|
128
|
+
| Revenue growth YoY | > 5% | 0-5% | Declining |
|
|
129
|
+
| Equity ratio | > 30% | 20-30% | < 20% |
|
|
130
|
+
|
|
131
|
+
## Step 5: Offer CRM Push
|
|
132
|
+
|
|
133
|
+
If the user seems satisfied with the results, offer:
|
|
134
|
+
|
|
135
|
+
> Want me to push these companies to your CRM? I can check which destinations are configured.
|
|
136
|
+
|
|
137
|
+
1. Use `list_destinations` to show available CRM destinations.
|
|
138
|
+
2. Confirm which companies to push (all results, or a selection).
|
|
139
|
+
3. Use `push_to_destination` with the chosen destination name and org numbers.
|
|
140
|
+
4. Report the results: how many were created, updated, or failed.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: push-to-crm
|
|
3
|
+
description: >
|
|
4
|
+
Push company data to a configured CRM destination (Attio, HubSpot, webhooks).
|
|
5
|
+
Use when the user wants to export or sync companies to their CRM.
|
|
6
|
+
allowed-tools: mcp__orakel__list_destinations, mcp__orakel__push_to_destination, mcp__orakel__manage_destination, mcp__orakel__search_companies, mcp__orakel__lookup_company
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Push to CRM
|
|
10
|
+
|
|
11
|
+
Export company data from Orakel to a configured CRM destination.
|
|
12
|
+
|
|
13
|
+
## Step 1: Identify Which Companies to Push
|
|
14
|
+
|
|
15
|
+
The user might provide companies in several ways:
|
|
16
|
+
|
|
17
|
+
- **Explicit org numbers:** Use those directly.
|
|
18
|
+
- **Coming from a previous search or prospect list:** Offer to push all results or let the user select specific ones.
|
|
19
|
+
- **A company name:** Use `search_companies` or `lookup_company` to resolve the org number first.
|
|
20
|
+
|
|
21
|
+
If the user says something vague like "push those companies," refer back to the most recent search results in the conversation.
|
|
22
|
+
|
|
23
|
+
Confirm the list before pushing:
|
|
24
|
+
|
|
25
|
+
> I'll push these 5 companies to your CRM:
|
|
26
|
+
> 1. Example AS (999888777)
|
|
27
|
+
> 2. Another AS (888777666)
|
|
28
|
+
> 3. ...
|
|
29
|
+
>
|
|
30
|
+
> Which destination should I use?
|
|
31
|
+
|
|
32
|
+
## Step 2: Check Available Destinations
|
|
33
|
+
|
|
34
|
+
Use `list_destinations` to show what's configured.
|
|
35
|
+
|
|
36
|
+
Present them simply:
|
|
37
|
+
|
|
38
|
+
> You have these CRM destinations set up:
|
|
39
|
+
> - **zero7-attio** (Attio CRM) - Active
|
|
40
|
+
> - **test-webhook** (Webhook) - Active
|
|
41
|
+
|
|
42
|
+
### If No Destinations Exist
|
|
43
|
+
|
|
44
|
+
Walk the user through creating one:
|
|
45
|
+
|
|
46
|
+
> You don't have any CRM destinations configured yet. I can set one up for you.
|
|
47
|
+
> What type of destination do you want?
|
|
48
|
+
> - **Attio** — pushes companies as Attio records
|
|
49
|
+
> - **Webhook** — sends company data to a URL you specify
|
|
50
|
+
|
|
51
|
+
Then use `manage_destination` with action "create":
|
|
52
|
+
|
|
53
|
+
- For Attio: Need the Attio API key and workspace slug
|
|
54
|
+
- For Webhook: Need the target URL and optionally an auth header
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
```
|
|
58
|
+
manage_destination(
|
|
59
|
+
action: "create",
|
|
60
|
+
name: "my-attio",
|
|
61
|
+
type: "attio",
|
|
62
|
+
config: { apiKey: "...", workspaceSlug: "..." }
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### If a Destination is Inactive
|
|
67
|
+
|
|
68
|
+
Mention it and offer to reactivate:
|
|
69
|
+
|
|
70
|
+
> The destination "old-webhook" exists but is inactive. Want me to reactivate it?
|
|
71
|
+
|
|
72
|
+
Use `manage_destination` with action "update" and `isActive: true`.
|
|
73
|
+
|
|
74
|
+
## Step 3: Execute the Push
|
|
75
|
+
|
|
76
|
+
Use `push_to_destination` with the destination name and array of org numbers.
|
|
77
|
+
|
|
78
|
+
**Important limits:**
|
|
79
|
+
- Push in batches if there are more than 50 companies (the API may time out on large batches).
|
|
80
|
+
- Each push is idempotent — pushing the same company twice will update rather than duplicate.
|
|
81
|
+
|
|
82
|
+
## Step 4: Report Results
|
|
83
|
+
|
|
84
|
+
The push response includes counts of created, updated, and failed records. Report clearly:
|
|
85
|
+
|
|
86
|
+
> Push complete:
|
|
87
|
+
> - **Created:** 3 new records in Attio
|
|
88
|
+
> - **Updated:** 2 existing records refreshed
|
|
89
|
+
> - **Failed:** 0
|
|
90
|
+
>
|
|
91
|
+
> All 5 companies are now in your CRM.
|
|
92
|
+
|
|
93
|
+
If any failed, show the org numbers and error messages so the user can investigate.
|
|
94
|
+
|
|
95
|
+
## Important Notes
|
|
96
|
+
|
|
97
|
+
- **Attio preserves brand names:** When pushing to Attio, company names from Orakel (legal names from Brreg) will not overwrite brand names that have been manually set in Attio. Only empty name fields get populated.
|
|
98
|
+
- **Data freshness:** Orakel pushes the latest data it has. Financial data may be up to a year old (depends on when the company filed).
|
|
99
|
+
- **Destination management:** Use `manage_destination` to create, update config, activate/deactivate, or delete destinations.
|