@pipeworx/mcp-macrostrat 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pipeworx
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 ADDED
@@ -0,0 +1,55 @@
1
+ # mcp-macrostrat
2
+
3
+ Macrostrat MCP — geologic map / column / unit data for North America and beyond.
4
+
5
+ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 808+ live data sources.
6
+
7
+ ## Tools
8
+
9
+ | Tool | Description |
10
+ |------|-------------|
11
+
12
+ ## Quick Start
13
+
14
+ Add to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
15
+
16
+ ```json
17
+ {
18
+ "mcpServers": {
19
+ "macrostrat": {
20
+ "url": "https://gateway.pipeworx.io/macrostrat/mcp"
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ Or connect to the full Pipeworx gateway for access to all 808+ data sources:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "pipeworx": {
32
+ "url": "https://gateway.pipeworx.io/mcp"
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Using with ask_pipeworx
39
+
40
+ Instead of calling tools directly, you can ask questions in plain English:
41
+
42
+ ```
43
+ ask_pipeworx({ question: "your question about Macrostrat data" })
44
+ ```
45
+
46
+ The gateway picks the right tool and fills the arguments automatically.
47
+
48
+ ## More
49
+
50
+ - [All tools and guides](https://github.com/pipeworx-io/examples)
51
+ - [pipeworx.io](https://pipeworx.io)
52
+
53
+ ## License
54
+
55
+ MIT
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@pipeworx/mcp-macrostrat",
3
+ "version": "0.1.0",
4
+ "description": "Macrostrat MCP — geologic map / column / unit data for North America and beyond.",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "macrostrat"],
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/pipeworx-io/mcp-macrostrat"
13
+ },
14
+ "scripts": {
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "devDependencies": {
18
+ "typescript": "^5.7.0"
19
+ }
20
+ }
package/server.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.pipeworx-io/macrostrat",
4
+ "title": "Macrostrat",
5
+ "description": "Macrostrat MCP — geologic map / column / unit data for North America and beyond.",
6
+ "version": "0.1.0",
7
+ "websiteUrl": "https://pipeworx.io/packs/macrostrat",
8
+ "repository": {
9
+ "url": "https://github.com/pipeworx-io/mcp-macrostrat",
10
+ "source": "github"
11
+ },
12
+ "remotes": [
13
+ {
14
+ "type": "streamable-http",
15
+ "url": "https://gateway.pipeworx.io/macrostrat/mcp"
16
+ }
17
+ ]
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,209 @@
1
+ interface McpToolDefinition {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: 'object';
6
+ properties: Record<string, unknown>;
7
+ required?: string[];
8
+ };
9
+ }
10
+
11
+ interface McpToolExport {
12
+ tools: McpToolDefinition[];
13
+ callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
14
+ meter?: { credits: number };
15
+ cost?: Record<string, unknown>;
16
+ provider?: string;
17
+ }
18
+
19
+ /**
20
+ * Macrostrat MCP — geologic map / column / unit data for North America and beyond.
21
+ *
22
+ * Wraps the keyless Macrostrat API (https://macrostrat.org/api). Find rock units by
23
+ * location + age or stratigraphic name, list stratigraphic columns near a point, and
24
+ * look up lithology definitions. Geologic ages are in millions of years (Ma). Keyless.
25
+ */
26
+
27
+
28
+ const BASE = 'https://macrostrat.org/api';
29
+ const UA = 'pipeworx/1.0 (+https://pipeworx.io)';
30
+
31
+ const tools: McpToolExport['tools'] = [
32
+ {
33
+ name: 'find_units',
34
+ description:
35
+ 'Find geologic rock units from Macrostrat by location + age, by stratigraphic name, or by lithology. ' +
36
+ 'Provide lat+lng (optionally with age in millions of years, Ma), or a strat_name (e.g. "Hell Creek"), or a lith (e.g. "granite"). ' +
37
+ 'Returns units with formation/group, stratigraphic column, bottom/top ages (Ma) and time intervals, lithology, and max thickness. Keyless. North America and beyond.',
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ lat: { type: 'number', description: 'Latitude (decimal degrees). Must be paired with lng.' },
42
+ lng: { type: 'number', description: 'Longitude (decimal degrees). Must be paired with lat.' },
43
+ age: { type: 'number', description: 'Geologic age in millions of years (Ma). Optional; used with lat+lng.' },
44
+ strat_name: { type: 'string', description: 'Stratigraphic name to match, e.g. "Hell Creek".' },
45
+ lith: { type: 'string', description: 'Lithology to match, e.g. "granite", "sandstone".' },
46
+ limit: { type: 'number', description: 'Max units to return (default 20).' },
47
+ },
48
+ },
49
+ },
50
+ {
51
+ name: 'find_columns',
52
+ description:
53
+ 'List Macrostrat stratigraphic columns near a geographic point. Provide lat and lng (decimal degrees). ' +
54
+ 'Returns the columns covering that location with column name, regional group, coordinates, unit count, and area. Keyless.',
55
+ inputSchema: {
56
+ type: 'object',
57
+ properties: {
58
+ lat: { type: 'number', description: 'Latitude (decimal degrees).' },
59
+ lng: { type: 'number', description: 'Longitude (decimal degrees).' },
60
+ },
61
+ required: ['lat', 'lng'],
62
+ },
63
+ },
64
+ {
65
+ name: 'define_lithology',
66
+ description:
67
+ 'Look up Macrostrat lithology definitions. Provide a lith name (e.g. "granite", "limestone") to match, or omit to list all lithologies (capped at 50). ' +
68
+ 'Returns lithology id, name, type, group, and class. Keyless.',
69
+ inputSchema: {
70
+ type: 'object',
71
+ properties: {
72
+ lith: { type: 'string', description: 'Lithology name to match, e.g. "granite". Omit to list all.' },
73
+ },
74
+ },
75
+ },
76
+ ];
77
+
78
+ async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
79
+ try {
80
+ switch (name) {
81
+ case 'find_units':
82
+ return await findUnits(args);
83
+ case 'find_columns':
84
+ return await findColumns(args);
85
+ case 'define_lithology':
86
+ return await defineLithology(args);
87
+ default:
88
+ return { error: `Unknown tool: ${name}` };
89
+ }
90
+ } catch (err) {
91
+ return { error: err instanceof Error ? err.message : String(err) };
92
+ }
93
+ }
94
+
95
+ async function macrostratGet(path: string): Promise<any[]> {
96
+ const res = await fetch(`${BASE}${path}`, {
97
+ headers: { Accept: 'application/json', 'User-Agent': UA },
98
+ });
99
+ if (!res.ok) {
100
+ throw new Error(`Macrostrat: ${res.status} ${(await res.text()).slice(0, 200)}`);
101
+ }
102
+ const json = (await res.json()) as any;
103
+ const data = json?.success?.data;
104
+ return Array.isArray(data) ? data : [];
105
+ }
106
+
107
+ function num(v: unknown): number | undefined {
108
+ if (typeof v === 'number') return v;
109
+ if (typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v))) return Number(v);
110
+ return undefined;
111
+ }
112
+
113
+ /** Normalize the `lith` field, which may be an array of objects, a string, or absent. */
114
+ function normalizeLith(lith: unknown): string | undefined {
115
+ if (Array.isArray(lith)) {
116
+ const names = Array.from(
117
+ new Set(
118
+ lith
119
+ .map((l) => (l && typeof l === 'object' ? (l as any).name : l))
120
+ .filter((n): n is string => typeof n === 'string' && n.trim() !== ''),
121
+ ),
122
+ );
123
+ return names.length ? names.join(', ') : undefined;
124
+ }
125
+ if (typeof lith === 'string') return lith || undefined;
126
+ return undefined;
127
+ }
128
+
129
+ async function findUnits(args: Record<string, unknown>): Promise<unknown> {
130
+ const lat = num(args.lat);
131
+ const lng = num(args.lng);
132
+ const age = num(args.age);
133
+ const stratName = typeof args.strat_name === 'string' ? args.strat_name.trim() : '';
134
+ const lith = typeof args.lith === 'string' ? args.lith.trim() : '';
135
+ const limit = num(args.limit) ?? 20;
136
+
137
+ const hasLatLng = lat !== undefined && lng !== undefined;
138
+ if (!hasLatLng && !stratName && !lith) {
139
+ return { error: 'provide lat+lng, strat_name, or lith' };
140
+ }
141
+
142
+ const params = new URLSearchParams();
143
+ if (hasLatLng) {
144
+ params.set('lat', String(lat));
145
+ params.set('lng', String(lng));
146
+ if (age !== undefined) params.set('age', String(age));
147
+ }
148
+ if (stratName) params.set('strat_name', stratName);
149
+ if (lith) params.set('lith', lith);
150
+ // response=long returns lithology, time intervals, and color fields per unit.
151
+ params.set('response', 'long');
152
+
153
+ const data = await macrostratGet(`/units?${params.toString()}`);
154
+ const units = data.slice(0, limit).map((u: any) => ({
155
+ unit_id: u.unit_id,
156
+ strat_name: u.strat_name ?? u.unit_name ?? u.strat_name_long,
157
+ formation: u.Fm,
158
+ group: u.Gp,
159
+ column: u.col_name,
160
+ col_id: u.col_id,
161
+ b_age: u.b_age,
162
+ t_age: u.t_age,
163
+ b_interval: u.b_int_name,
164
+ t_interval: u.t_int_name,
165
+ lith: normalizeLith(u.lith),
166
+ max_thick: u.max_thick,
167
+ }));
168
+ return { count: units.length, units };
169
+ }
170
+
171
+ async function findColumns(args: Record<string, unknown>): Promise<unknown> {
172
+ const lat = num(args.lat);
173
+ const lng = num(args.lng);
174
+ if (lat === undefined || lng === undefined) {
175
+ return { error: 'lat and lng are required' };
176
+ }
177
+ const params = new URLSearchParams();
178
+ params.set('lat', String(lat));
179
+ params.set('lng', String(lng));
180
+
181
+ const data = await macrostratGet(`/columns?${params.toString()}`);
182
+ const columns = data.map((c: any) => ({
183
+ col_id: c.col_id,
184
+ col_name: c.col_name,
185
+ group: c.col_group ?? c.group,
186
+ lat: num(c.lat) ?? c.lat,
187
+ lng: num(c.lng) ?? c.lng,
188
+ units: c.t_units,
189
+ area: c.col_area ?? c.area,
190
+ }));
191
+ return { count: columns.length, columns };
192
+ }
193
+
194
+ async function defineLithology(args: Record<string, unknown>): Promise<unknown> {
195
+ const lith = typeof args.lith === 'string' ? args.lith.trim() : '';
196
+ const query = lith ? `lith=${encodeURIComponent(lith)}` : 'all';
197
+ const data = await macrostratGet(`/defs/lithologies?${query}`);
198
+ const sliced = lith ? data : data.slice(0, 50);
199
+ const lithologies = sliced.map((l: any) => ({
200
+ lith_id: l.lith_id,
201
+ name: l.name,
202
+ type: l.type,
203
+ group: l.group,
204
+ class: l.class,
205
+ }));
206
+ return { count: lithologies.length, lithologies };
207
+ }
208
+
209
+ export default { tools, callTool, meter: { credits: 1 } } satisfies McpToolExport;
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }