Instrument Data To Allotrope
aws-samples/amazon-bedrock-agents-healthcare-lifesciences
Convert laboratory instrument output files (PDF, CSV, Excel, TXT) to Allotrope Simple Model (ASM) JSON format or flattened 2D CSV.
A skill your agent uses whenever the user mentions IP geolocation feeds, RFC 8805, geofeeds, or wants help creating, tuning, validating, or publishing a self-published IP geolocation feed in CSV…
$ npx skills add github/awesome-copilot --skill geofeed-tuner -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install github/awesome-copilot geofeed-tuner --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/github/awesome-copilot.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/geofeed-tuner .claude/skills/geofeed-tuner && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "geofeed-tuner" agent skill from https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tuner into .claude/skills/geofeed-tuner/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "geofeed-tuner", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tunerType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add github/awesome-copilot --skill geofeed-tuner -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install github/awesome-copilot geofeed-tuner --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/github/awesome-copilot.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/geofeed-tuner .agents/skills/geofeed-tuner && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "geofeed-tuner" agent skill from https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tuner into .agents/skills/geofeed-tuner/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "geofeed-tuner", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add github/awesome-copilot --skill geofeed-tuner -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install github/awesome-copilot geofeed-tuner --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/github/awesome-copilot.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/geofeed-tuner .cursor/skills/geofeed-tuner && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "geofeed-tuner" agent skill from https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tuner into .cursor/skills/geofeed-tuner/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "geofeed-tuner", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/github/awesome-copilot.git --path skills/geofeed-tuner--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add github/awesome-copilot --skill geofeed-tuner -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install github/awesome-copilot geofeed-tuner --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/github/awesome-copilot.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/geofeed-tuner .gemini/skills/geofeed-tuner && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "geofeed-tuner" agent skill from https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tuner into .gemini/skills/geofeed-tuner/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "geofeed-tuner", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install github/awesome-copilot geofeed-tunerInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add github/awesome-copilot --skill geofeed-tuner -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/github/awesome-copilot.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/geofeed-tuner .github/skills/geofeed-tuner && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "geofeed-tuner" agent skill from https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tuner into .github/skills/geofeed-tuner/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "geofeed-tuner", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add github/awesome-copilot --skill geofeed-tuner -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install github/awesome-copilot geofeed-tuner --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/github/awesome-copilot.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/geofeed-tuner .opencode/skills/geofeed-tuner && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "geofeed-tuner" agent skill from https://github.com/github/awesome-copilot/tree/main/skills/geofeed-tuner into .opencode/skills/geofeed-tuner/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "geofeed-tuner", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
geofeed-tunerA skill your agent uses whenever the user mentions IP geolocation feeds, RFC 8805, geofeeds, or wants help creating, tuning, validating, or publishing a self-published IP geolocation feed in CSV…
Geofeed Tuner is an agent skill from github/awesome-copilot, published by the product's own GitHub organization. Use this skill whenever the user mentions IP geolocation feeds, RFC 8805, geofeeds, or wants help creating, tuning, validating, or publishing a self-published IP geolocation feed in CSV format. Intended user audience is a network operator, ISP, mobile carrier, cloud provider, hosting company, IXP, or satellite provider asking about IP geolocation accuracy, or geofeed authoring best practices. Helps create, refine, and improve CSV-format IP geolocation feeds with opinionated recommendations beyond RFC 8805…
Its SKILL.md is about 12k tokens, which your agent loads only when the skill is triggered. The skill folder holds 12 other files, including scripts, reference files and assets (for example `assets/iso3166-1.json`, `assets/iso3166-2.json` and `assets/small-territories.json`). Compatibility notes: Requires Python 3
It sits in Documents & Office, covering CSV and tabular files. The repository describes itself as: Community-contributed instructions, agents, skills, and configurations to help you make the most of GitHub Copilot. The licence is Apache-2.0.
6 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 727ff2e. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 1 file in scripts/, which the agent can run.
From the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
mcp.fastah.aiFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Requires Python 3
From compatibility in the SKILL.md frontmatter.
Geofeed Tuner loads about 12k tokens when it runs, and up to ~21k if it reads all its reference files. Until then it costs about 161 tokens; SKILL.md has 4,927 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from github/awesome-copilot at commit 727ff2e, republished under its Apache-2.0 licence (© github). 4,927 words, ~12,160 tokens.
.claude/skills/geofeed-tuner/SKILL.md (or your agent's skills folder). This skill also uses 7 other files; get the full folder from GitHub.This skill helps you create and improve IP geolocation feeds in CSV format by:
This skill uses a clear separation between distribution files (read-only) and working files (generated at runtime).
The following directories contain static distribution assets. Do not create, modify, or delete files in these directories:
| Directory | Purpose |
|---|---|
assets/ | Static data files (ISO codes, examples) |
references/ | RFC specifications and code snippets for reference |
scripts/ | Executable code and HTML template files for reports |
All generated, temporary, and output files go in these directories:
| Directory | Purpose |
|---|---|
run/ | Working directory for all agent-generated content |
run/data/ | Downloaded CSV files from remote URLs |
run/report/ | Generated HTML tuning reports |
assets/, references/, or scripts/ — these are part of the skill distribution and must remain unchanged../run/data/../run/report/../run/.run/ directory may be cleared between sessions; do not store permanent data there../run/ must be executed with the skill root directory (the directory containing SKILL.md) as the current working directory, so that relative paths like assets/iso3166-1.json and ./run/data/report-data.json resolve correctly. Do not cd into ./run/ before running scripts.All phases must be executed in order, from Phase 1 through Phase 6. Each phase depends on the successful completion of the previous phase. For example, structure checks must complete before quality analysis can run.
The phases are summarized below. The agent must follow the detailed steps outlined further in each phase section.
| Phase | Name | Description |
|---|---|---|
| 1 | Understand the Standard | Review the key requirements of RFC 8805 for self-published IP geolocation feeds |
| 2 | Gather Input | Collect IP subnet data from local files or remote URLs |
| 3 | Checks & Suggestions | Validate CSV structure, analyze IP prefixes, and check data quality |
| 4 | Tuning Data Lookup | Use Fastah's MCP tool to retrieve tuning data for improving geolocation accuracy |
| 5 | Generate Tuning Report | Create an HTML report summarizing the analysis and suggestions |
| 6 | Final Review | Verify consistency and completeness of the report data |
Do not skip phases. Each phase provides critical checks or data transformations required by subsequent stages.
Before executing each phase, the agent MUST generate a visible TODO checklist.
The plan MUST:
The key requirements from RFC 8805 that this skill enforces are summarized below. Use this summary as your working reference. Only consult the full RFC 8805 text for edge cases, ambiguous situations, or when the user asks a standards question not covered here.
Purpose: A self-published IP geolocation feed lets network operators publish authoritative location data for their IP address space in a simple CSV format, allowing geolocation providers to incorporate operator-supplied corrections.
CSV Column Order (Sections 2.1.1.1–2.1.1.5):
| Column | Field | Required | Notes |
|---|---|---|---|
| 1 | ip_prefix | Yes | CIDR notation; IPv4 or IPv6; must be a network address |
| 2 | alpha2code | No | ISO 3166-1 alpha-2 country code; empty or "ZZ" = do-not-geolocate |
| 3 | region | No | ISO 3166-2 subdivision code (e.g., US-CA) |
| 4 | city | No | Free-text city name; no authoritative validation set |
| 5 | postal_code | No | Deprecated — must be left empty or absent |
Structural rules:
# (including the header, if present).#.192.168.1.1/24 is invalid; use 192.168.1.0/24).Do-not-geolocate: An entry with an empty alpha2code or case-insensitive ZZ (irrespective of values of region/city) is an explicit signal that the operator does not want geolocation applied to that prefix.
Postal codes deprecated (Section 2.1.1.5): The fifth column must not contain postal or ZIP codes. They are too fine-grained for IP-range mapping and raise privacy concerns.
If the user has not already provided a list of IP subnets or ranges (sometimes referred to as inetnum or inet6num), prompt them to supply it. Accepted input formats:
If the input is a remote URL:
./run/data/ before processing.Feed URL is not reachable: HTTP {status_code}. Please verify the URL is publicly accessible.If the input is a local file, process it directly without downloading.
Encoding detection and normalization:
UnicodeDecodeError is raised, try utf-8-sig (UTF-8 with BOM), then latin-1.Unable to decode input file. Please save it as UTF-8 and try again../run/data/report-data.jsonThe JSON structure below is IMMUTABLE during Phase 3. Phase 4 will later add a TunedEntry object to each object in Entries — this is the only permitted schema extension and happens in a separate phase.
JSON keys map directly to template placeholders like {{.CountryCode}}, {{.HasError}}, etc.
{
"InputFile": "",
"Timestamp": 0,
"TotalEntries": 0,
"IpV4Entries": 0,
"IpV6Entries": 0,
"InvalidEntries": 0,
"Errors": 0,
"Warnings": 0,
"OK": 0,
"Suggestions": 0,
"CityLevelAccuracy": 0,
"RegionLevelAccuracy": 0,
"CountryLevelAccuracy": 0,
"DoNotGeolocate": 0,
"Entries": [
{
"Line": 0,
"IPPrefix": "",
"CountryCode": "",
"RegionCode": "",
"City": "",
"Status": "",
"IPVersion": "",
"Messages": [
{
"ID": "",
"Type": "",
"Text": "",
"Checked": false
}
],
"HasError": false,
"HasWarning": false,
"HasSuggestion": false,
"DoNotGeolocate": false,
"GeocodingHint": "",
"Tunable": false
}
]
}Field definitions:
Top-level metadata:
InputFile: The original input source, either a local filename or a remote URL.Timestamp: Milliseconds since Unix epoch when the tuning was performed.TotalEntries: Total number of data rows processed (excluding comment and blank lines).IpV4Entries: Count of entries that are IPv4 subnets.IpV6Entries: Count of entries that are IPv6 subnets.InvalidEntries: Count of entries that failed IP prefix parsing and CSV parsing.Errors: Total entries whose Status is ERROR.Warnings: Total entries whose Status is WARNING.OK: Total entries whose Status is OK.Suggestions: Total entries whose Status is SUGGESTION.CityLevelAccuracy: Count of valid entries where City is non-empty.RegionLevelAccuracy: Count of valid entries where RegionCode is non-empty and City is empty.CountryLevelAccuracy: Count of valid entries where CountryCode is non-empty, RegionCode is empty, and City is empty.DoNotGeolocate (metadata): Count of valid entries where CountryCode, RegionCode, and City are all empty.Entry fields:
Entries: Array of objects, one per data row, with the following per-entry fields:Line: 1-based line number in the original CSV (counting all lines including comments and blanks).IPPrefix: The normalized IP prefix in CIDR slash notation.CountryCode: The ISO 3166-1 alpha-2 country code, or empty string.RegionCode: The ISO 3166-2 region code (e.g., US-CA), or empty string.City: The city name, or empty string.Status: Highest severity assigned: ERROR > WARNING > SUGGESTION > OK.IPVersion: "IPv4" or "IPv6" based on the parsed IP prefix.Messages: Array of message objects, each with:ID: String identifier from the Validation Rules Reference table below (e.g., "1101", "3301").Type: The severity type: "ERROR", "WARNING", or "SUGGESTION".Text: The human-readable validation message string.Checked: true if the validation rule is auto-tunable (Tunable: true in the reference table), false otherwise. Controls whether the checkbox in the report is checked or disabled.HasError: true if any message has Type "ERROR".HasWarning: true if any message has Type "WARNING".HasSuggestion: true if any message has Type "SUGGESTION".DoNotGeolocate (entry): true if CountryCode is empty or "ZZ" — the entry is an explicit do-not-geolocate signal.GeocodingHint: Always empty string "" in Phase 3. Reserved for future use.Tunable: true if any message in the entry has Checked: true. Computed as logical OR across all messages' Checked values. This flag drives the "Tune" button visibility in the report.When adding messages to an entry, use the ID, Type, Text, and Checked values from this table.
| ID | Type | Text | Checked | Condition Reference |
|---|---|---|---|---|
1101 | ERROR | IP prefix is empty | false | IP Prefix Analysis: empty |
1102 | ERROR | Invalid IP prefix: unable to parse as IPv4 or IPv6 network | false | IP Prefix Analysis: invalid syntax |
1103 | ERROR | Non-public IP range is not allowed in an RFC 8805 feed | false | IP Prefix Analysis: non-public |
3101 | SUGGESTION | IPv4 prefix is unusually large and may indicate a typo | false | IP Prefix Analysis: IPv4 < /22 |
3102 | SUGGESTION | IPv6 prefix is unusually large and may indicate a typo | false | IP Prefix Analysis: IPv6 < /64 |
1201 | ERROR | Invalid country code: not a valid ISO 3166-1 alpha-2 value | true | Country Code Analysis: invalid |
1301 | ERROR | Invalid region format; expected COUNTRY-SUBDIVISION (e.g., US-CA) | true | Region Code Analysis: bad format |
1302 | ERROR | Invalid region code: not a valid ISO 3166-2 subdivision | true | Region Code Analysis: unknown code |
1303 | ERROR | Region code does not match the specified country code | true | Region Code Analysis: mismatch |
1401 | ERROR | Invalid city name: placeholder value is not allowed | false | City Name Analysis: placeholder |
1402 | ERROR | Invalid city name: abbreviated or code-based value detected | true | City Name Analysis: abbreviation |
2401 | WARNING | City name formatting is inconsistent; consider normalizing the value | true | City Name Analysis: formatting |
1501 | ERROR | Postal codes are deprecated by RFC 8805 and must be removed for privacy reasons | true | Postal Code Check |
3301 | SUGGESTION | Region is usually unnecessary for small territories; consider removing the region value | true | Tuning: small territory region |
3402 | SUGGESTION | City-level granularity is usually unnecessary for small territories; consider removing the city value | true | Tuning: small territory city |
3303 | SUGGESTION | Region code is recommended when a city is specified; choose a region from the dropdown | true | Tuning: missing region with city |
3104 | SUGGESTION | Confirm whether this subnet is intentionally marked as do-not-geolocate or missing location data | true | Tuning: unspecified geolocation |
When a validation check matches, add a message to the entry's Messages array using the values from the reference table:
entry["Messages"].append({
"ID": "1201", # From the table
"Type": "ERROR", # From the table
"Text": "Invalid country code: not a valid ISO 3166-1 alpha-2 value", # From the table
"Checked": True # From the table (True = tunable)
})After populating all messages for an entry, derive the entry-level flags:
entry["HasError"] = any(m["Type"] == "ERROR" for m in entry["Messages"])
entry["HasWarning"] = any(m["Type"] == "WARNING" for m in entry["Messages"])
entry["HasSuggestion"] = any(m["Type"] == "SUGGESTION" for m in entry["Messages"])
entry["Tunable"] = any(m["Checked"] for m in entry["Messages"])Accuracy levels are mutually exclusive. Assign each valid (non-ERROR, non-invalid) entry to exactly one bucket based on the most granular non-empty geo field:
| Condition | Bucket |
|---|---|
City is non-empty | CityLevelAccuracy |
RegionCode non-empty AND City is empty | RegionLevelAccuracy |
CountryCode non-empty, RegionCode and City empty | CountryLevelAccuracy |
DoNotGeolocate (entry) is true | DoNotGeolocate (metadata) |
Do not count entries with HasError: true or entries in InvalidEntries in any accuracy bucket.
The agent MUST NOT:
If a value is unknown, leave it empty — never invent data.
This phase verifies that your feed is well-formed and parseable. Critical structural errors must be resolved before the tuner can analyze geolocation quality.
This subsection defines rules for CSV-formatted input files used for IP geolocation feeds. The goal is to ensure the file can be parsed reliably and normalized into a consistent internal representation.
CSV Structure Checks
If pandas is available, use it for CSV parsing.
Otherwise, fall back to Python's built-in csv module.
Ensure the CSV contains exactly 4 or 5 logical columns.
Comment lines are allowed.
A header row may or may not be present.
If no header row exists, assume the implicit column order:
ip_prefix, alpha2code, region, city, postal code (deprecated)Refer to the example input file:
assets/example/01-user-input-rfc8805-feed.csv
CSV Cleansing and Normalization
Clean and normalize the CSV using Python logic equivalent to the following operations:
Comments
#.#../run/data/comments.json{ "4": "# It's OK for small city states to leave state ISO2 code unspecified" }Notes
pandas and built-in csv) must write output using
the utf-8-sig encoding to ensure a UTF-8 BOM is present.Check that the IPPrefix field is present and non-empty for each entry.
Check for duplicate IPPrefix values across entries.
If duplicates are found, stop the skill and report to the user with the message: Duplicate IP prefix detected: {ip_prefix_value} appears on lines {line_numbers}
If no duplicates are found, continue with the analysis.
Checks
references/ folder./32./128.ERROR
Report the following conditions as ERROR:
Invalid subnet syntax
1102Non-public address space
is_private and related address properties as shown in ./references.1103SUGGESTION
Report the following conditions as SUGGESTION:
Overly large IPv6 subnets
/643102Overly large IPv4 subnets
/223101Analyze the accuracy and consistency of geolocation data:
This phase runs after structural checks pass.
Use the locally available data table ISO3166-1 for checking.
alpha_2: two-letter country codename: short country nameflag: flag emojiCountryCode values for an RFC 8805 CSV.Check the entry's CountryCode (RFC 8805 Section 2.1.1.2, column alpha2code) against the alpha_2 attribute.
Sample code is available in the references/ directory.
If a country is found in assets/small-territories.json, mark the entry internally as a small territory. This flag is used in later checks and suggestions but is not stored in the output JSON (it is transient validation state).
Note: small-territories.json contains some historic/disputed codes (AN, CS, XK) that are not present in iso3166-1.json. An entry using one of these as its CountryCode will fail the country code validation (ERROR) even though it matches as a small territory. The country code ERROR takes precedence — do not suppress it based on the small-territory flag.
ERROR
CountryCode is present but not found in the alpha_2 set1201SUGGESTION
Report the following conditions as SUGGESTION:
Unspecified geolocation for subnet
CountryCode, RegionCode, City) are empty for a subnet.DoNotGeolocate = true for the entry.CountryCode to ZZ for the entry.3104Use the locally available data table ISO3166-2 for checking.
code: subdivision code prefixed with country code (e.g., US-CA)name: short subdivision nameRegionCode values for an RFC 8805 CSV.If a RegionCode value is provided (RFC 8805 Section 2.1.1.3):
{COUNTRY}-{SUBDIVISION} (e.g., US-CA, AU-NSW).code attribute (already prefixed with the country code).Small-territory exception: If the entry is a small territory and the RegionCode value equals the entry's CountryCode (e.g., SG as both country and region for Singapore), treat the region as acceptable — skip all region validation checks for this entry. Small territories are effectively city-states with no meaningful ISO 3166-2 administrative subdivisions.
ERROR
RegionCode does not match {COUNTRY}-{SUBDIVISION} and the small-territory exception does not apply1301RegionCode value is not found in the code set and the small-territory exception does not apply1302RegionCode does not match CountryCode1303City names are validated using heuristic checks only.
There is currently no authoritative dataset available for validating city names.
ERROR
Report the following conditions as ERROR:
Placeholder or non-meaningful values
undefinedPlease selectnullN/ATBDunknown1401Truncated names, abbreviations, or airport codes
LAFrftsin01LHRSINMAA1402WARNING
HongKong vs Hong Kong2401RFC 8805 Section 2.1.1.5 explicitly deprecates postal or ZIP codes.
Postal codes can represent very small populations and are not considered privacy-safe for mapping IP address ranges, which are statistical in nature.
ERROR
1501This phase applies opinionated recommendations beyond RFC 8805, learned from real-world geofeed deployments, that improve accuracy and usability.
Report the following conditions as SUGGESTION:
Region or city specified for small territory
RegionCode is non-empty ORCity is non-empty.3301 (for region), 3402 (for city)Missing region code when city is specified
City is non-emptyRegionCode is empty3303Lookup all the Entries using Fastah's rfc8805-row-place-search tool.
TunedEntry: {} for all affected entries. Do not block report generation. Notify the user clearly: Tuning data lookup unavailable; the report will show validation results only.Load the dataset from: ./run/data/report-data.json
Entries array. Each entry will be used to build the MCP lookup payload.Reduce server requests by deduplicating identical entries:
Entries, compute a content hash (hash of CountryCode + RegionCode + City).{ contentHash -> { rowKey, payload, entryIndices: [] } }. rowKey is a UUID that will be sent to the MCP server for matching responses.Entries to that deduplication entry's entryIndices array.Build request batches:
[{ rowKey, payload, entryIndices }, ...] to match responses back by rowKey.rowKey field with each payload object:[
{"rowKey": "550e8400-e29b-41d4-a716-446655440000", "countryCode":"CA","regionCode":"CA-ON","cityName":"Toronto"},
{"rowKey": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "countryCode":"IN","regionCode":"IN-KA","cityName":"Bangalore"},
{"rowKey": "6ba7b811-9dad-11d1-80b4-00c04fd430c8", "countryCode":"IN","regionCode":"IN-KA"}
]rowKey field to the corresponding deduplication entry to retrieve all associated entryIndices.Rules:
./run/data/mcp-server-payload.jsonmcp.json style configuration of Fastah MCP server is as follows: "fastah-ip-geofeed": {
"type": "http",
"url": "https://mcp.fastah.ai/mcp"
}Server: https://mcp.fastah.ai/mcp
Tool and its Schema: before the first tools/call, the agent MUST send a tools/list request to read the input and output schema for rfc8805-row-place-search.
Use the discovered schema as the authoritative source for field names, types, and constraints.
The following is an illustrative example only; always defer to the schema returned by tools/list:
[
{"rowKey": "550e8400-...", "countryCode":"CA", ...},
{"rowKey": "690e9301-...", "countryCode":"ZZ", ...}
]Open ./run/data/mcp-server-payload.json and send all deduplicated entries with their rowKeys.
If there are more than 1000 deduplicated entries after deduplication, split into multiple requests of 1000 entries each.
The server will respond with the same rowKey field in each response for mapping back.
Do NOT use local data.
./run/data/report-data.json and the deduplication map (held in memory from Step 1, or re-derived from the payload file).rowKey from the response.entryIndices array associated with that rowKey from the deduplication map.entryIndices, attach the best match to Entries[index].Create the field on each affected entry if it does not exist. Remap the MCP API response keys to Go struct field names:
"TunedEntry": {
"Name": "",
"CountryCode": "",
"RegionCode": "",
"PlaceType": "",
"H3Cells": [],
"BoundingBox": []
}The TunedEntry field is a single object (not an array). It holds the best match from the MCP server.
MCP response key → JSON key mapping:
| MCP API response key | JSON key |
|---|---|
placeName | Name |
countryCode | CountryCode |
stateCode | RegionCode |
placeType | PlaceType |
h3Cells | H3Cells |
boundingBox | BoundingBox |
Entries with no UUID match (i.e. the MCP server returned no response for their UUID) must receive an empty TunedEntry: {} object — never leave the field absent.
./run/data/report-data.jsonGenerate a self-contained HTML report by rendering the template at ./scripts/templates/index.html with data from ./run/data/report-data.json and ./run/data/comments.json.
Write the completed report to ./run/report/geofeed-report.html. After generating, attempt to open it in the system's default browser (e.g., webbrowser.open()). If running in a headless environment, CI pipeline, or remote container where no browser is available, skip the browser step and instead present the file path to the user so they can open or download it.
The template uses Go html/template syntax ({{.Field}}, {{range}}, {{if eq}}, etc.). Write a Python script that reads the template, builds a rendering context from the JSON data files, and processes the template placeholders to produce final HTML. Do not modify the template file itself — all processing happens in the Python script at render time.
Replace each {{.Metadata.X}} placeholder in the template with the corresponding value from report-data.json. Since JSON keys match the template placeholder, the mapping is direct — {{.Metadata.InputFile}} maps to the InputFile JSON key, etc.
| Template placeholder | JSON key (report-data.json) |
|---|---|
{{.Metadata.InputFile}} | InputFile |
{{.Metadata.Timestamp}} | Timestamp |
{{.Metadata.TotalEntries}} | TotalEntries |
{{.Metadata.IpV4Entries}} | IpV4Entries |
{{.Metadata.IpV6Entries}} | IpV6Entries |
{{.Metadata.InvalidEntries}} | InvalidEntries |
{{.Metadata.Errors}} | Errors |
{{.Metadata.Warnings}} | Warnings |
{{.Metadata.Suggestions}} | Suggestions |
{{.Metadata.OK}} | OK |
{{.Metadata.CityLevelAccuracy}} | CityLevelAccuracy |
{{.Metadata.RegionLevelAccuracy}} | RegionLevelAccuracy |
{{.Metadata.CountryLevelAccuracy}} | CountryLevelAccuracy |
{{.Metadata.DoNotGeolocate}} | DoNotGeolocate (metadata) |
Note on {{.Metadata.Timestamp}}: This placeholder appears inside a JavaScript new Date(...) call. Replace it with the raw integer value (no HTML escaping needed for a numeric literal inside <script>). All other metadata values should be HTML-escaped since they appear inside HTML element text.
Locate this pattern in the template:
const commentMap = {{.Comments}};Replace {{.Comments}} with the serialized JSON object from ./run/data/comments.json. The JSON is embedded directly as a JavaScript object literal (not inside a string), so no extra escaping is needed:
comments_json = json.dumps(comments)
template = template.replace("{{.Comments}}", comments_json)The template contains a {{range .Entries}}...{{end}} block inside <tbody id="entriesTableBody">. Process it as follows:
{{end}} tags (from {{if eq .Status ...}}, {{if .Checked}}, and {{range .Messages}}). A naive non-greedy match like \{\{range \.Entries\}\}(.*?)\{\{end\}\} will match the first inner {{end}}, truncating the block. Instead, anchor the outer {{end}} to the </tbody> that follows it:m = re.search(
r'\{\{range \.Entries\}\}(.*?)\{\{end\}\}\s*</tbody>',
template,
re.DOTALL,
)
entry_body = m.group(1) # template text for one entry iteration<tr> rows and the nested {{range .Messages}}...{{end}}.report-data.json's Entries array.{{range .Entries}} through </tbody>) with the concatenated expanded HTML followed by </tbody>.Processing order for each entry (innermost constructs first to avoid {{end}} confusion):
{{if eq .Status ...}}...{{end}} conditionals (status badge class and icon).{{if .Checked}}...{{end}} conditional (message checkbox).{{range .Messages}}...{{end}} inner range.{{.Field}} placeholders.Within the range block body, replace these placeholders for each entry. Since JSON keys match the template placeholder, the template placeholder {{.X}} maps directly to JSON key X:
| Template placeholder | JSON key (Entries[]) | Notes |
|---|---|---|
{{.Line}} | Line | Direct integer value |
{{.IPPrefix}} | IPPrefix | HTML-escaped |
{{.CountryCode}} | CountryCode | HTML-escaped |
{{.RegionCode}} | RegionCode | HTML-escaped |
{{.City}} | City | HTML-escaped |
{{.Status}} | Status | HTML-escaped |
{{.HasError}} | HasError | Lowercase string: "true" or "false" |
{{.HasWarning}} | HasWarning | Lowercase string: "true" or "false" |
{{.HasSuggestion}} | HasSuggestion | Lowercase string: "true" or "false" |
{{.GeocodingHint}} | GeocodingHint | Empty string "" |
{{.DoNotGeolocate}} | DoNotGeolocate | "true" or "false" |
{{.Tunable}} | Tunable | "true" or "false" |
{{.TunedEntry.CountryCode}} | TunedEntry.CountryCode | "" if TunedEntry is empty {} |
{{.TunedEntry.RegionCode}} | TunedEntry.RegionCode | "" if TunedEntry is empty {} |
{{.TunedEntry.Name}} | TunedEntry.Name | "" if TunedEntry is empty {} |
{{.TunedEntry.H3Cells}} | TunedEntry.H3Cells | Bracket-wrapped space-separated; "[]" if empty (see format below) |
{{.TunedEntry.BoundingBox}} | TunedEntry.BoundingBox | Bracket-wrapped space-separated; "[]" if empty (see format below) |
data-h3-cells and data-bounding-box format: These are NOT JSON arrays. They are bracket-wrapped, space-separated values. Do not use JSON serialization (no quotes around string elements, no commas between numbers). Examples:
[836752fffffffff 836755fffffffff] — correct["836752fffffffff","836755fffffffff"] — WRONG, quotes will break parsing[-71.70 10.73 -71.52 10.55] — correct[] — correct for emptyProcess these BEFORE replacing simple {{.Field}} placeholders — otherwise the {{end}} markers get consumed and the regex won't match.
The template uses {{if eq .Status "..."}} conditionals for the status badge CSS class and icon. Evaluate these by checking the entry's status value and keeping only the matching branch text.
The status badge line contains two {{if eq .Status ...}}...{{end}} blocks on a single line — one for the CSS class, one for the icon. Use re.sub with a callback to resolve all occurrences:
STATUS_CSS = {"ERROR": "error", "WARNING": "warning", "SUGGESTION": "suggestion", "OK": "ok"}
STATUS_ICON = {
"ERROR": "bi-x-circle-fill",
"WARNING": "bi-exclamation-triangle-fill",
"SUGGESTION": "bi-lightbulb-fill",
"OK": "bi-check-circle-fill",
}
def resolve_status_if(match_obj, status):
"""Pick the branch matching `status` from a {{if eq .Status ...}}...{{end}} block."""
block = match_obj.group(0)
# Try each branch: {{if eq .Status "X"}}val{{else if ...}}val{{else}}val{{end}}
for st, val in [("ERROR",), ("WARNING",), ("SUGGESTION",)]:
# not needed to parse generically — just map from the known patterns
...A simpler approach: since there are exactly two known patterns, replace them as literal strings:
css_class = STATUS_CSS.get(status, "ok")
icon_class = STATUS_ICON.get(status, "bi-check-circle-fill")
body = body.replace(
'{{if eq .Status "ERROR"}}error{{else if eq .Status "WARNING"}}warning{{else if eq .Status "SUGGESTION"}}suggestion{{else}}ok{{end}}',
css_class,
)
body = body.replace(
'{{if eq .Status "ERROR"}}bi-x-circle-fill{{else if eq .Status "WARNING"}}bi-exclamation-triangle-fill{{else if eq .Status "SUGGESTION"}}bi-lightbulb-fill{{else}}bi-check-circle-fill{{end}}',
icon_class,
)This avoids regex entirely and is safe because these exact strings appear verbatim in the template.
The {{range .Messages}}...{{end}} block contains a nested {{if .Checked}} checked{{else}} disabled{{end}} conditional, so its inner {{end}} would cause a simple non-greedy regex to match too early. Anchor the regex to </td> (the tag immediately after the messages range closing {{end}}) to capture the full block body:
msg_match = re.search(
r'\{\{range \.Messages\}\}(.*?)\{\{end\}\}\s*(?=</td>)',
body, re.DOTALL
)The lookahead (?=</td>) ensures the regex skips past the checkbox conditional's {{end}} (which is followed by >, not </td>) and matches only the range-closing {{end}} (which is followed by whitespace then </td>).
For each message in the entry's Messages array, clone the captured block body and expand it:
Resolve the checkbox conditional per message (must happen before simple placeholder replacement to remove the nested {{end}}):
if msg.get("Checked"):
msg_body = msg_body.replace(
'{{if .Checked}} checked{{else}} disabled{{end}}', ' checked'
)
else:
msg_body = msg_body.replace(
'{{if .Checked}} checked{{else}} disabled{{end}}', ' disabled'
)Replace message field placeholders:
| Template placeholder | Source | Notes |
|---|---|---|
{{.ID}} | Messages[i].ID | Direct string value from JSON |
{{.Text}} | Messages[i].Text | HTML-escaped |
Concatenate all expanded message blocks and replace the original {{range .Messages}}...{{end}} match (msg_match.group(0)) with the result:
body = body[:msg_match.start()] + "".join(expanded_msgs) + body[msg_match.end():]If Messages is empty, replace the entire matched region with an empty string (no message divs — only the issues header remains).
leaflet, h3-js, bootstrap-icons, Raleway font).<, >, &, ") to prevent rendering issues.commentMap is embedded as a direct JavaScript object literal (not inside a string), so no JS string escaping is needed — just emit valid JSON.Perform a final verification pass using concrete, checkable assertions before presenting results to the user.
Check 1 — Entry count integrity
len(entries) in report-data.json == data_row_countRow count mismatch: input has {N} data rows but report contains {M} entries.Check 2 — Summary counter integrity
Status field. An entry with both HasError: true and HasWarning: true is counted only in Errors, never in Warnings. This is equivalent to counting by the entry's Status field.Errors == sum(1 for e in Entries if e['HasError'])Warnings == sum(1 for e in Entries if e['HasWarning'] and not e['HasError'])Suggestions == sum(1 for e in Entries if e['HasSuggestion'] and not e['HasError'] and not e['HasWarning'])OK == sum(1 for e in Entries if not e['HasError'] and not e['HasWarning'] and not e['HasSuggestion'])Errors + Warnings + Suggestions + OK == TotalEntries - InvalidEntriesCheck 3 — Accuracy bucket integrity
CityLevelAccuracy + RegionLevelAccuracy + CountryLevelAccuracy + DoNotGeolocate == TotalEntries - InvalidEntriesHasError: true", but the Check 3 formula above uses TotalEntries - InvalidEntries (which still includes ERROR entries). This means ERROR entries (those that parsed as valid IPs but failed validation) are counted in accuracy buckets by their geo-field presence. Only InvalidEntries (unparsable IP prefixes) are excluded. Follow the Check 3 formula as the authoritative rule.Check 4 — No duplicate line numbers
Line values in Entries are unique.Check 5 — TunedEntry completeness
Entries has a TunedEntry key (even if its value is {})."TunedEntry": {} to any entry missing the key, then re-save report-data.json.Check 6 — Report file is present and non-empty
./run/report/geofeed-report.html was written and has a file size greater than zero bytes.© github, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 7 other files (scripts, references, assets) in skills/geofeed-tuner of github/awesome-copilot.
Open the folder on GitHubat commit 727ff2e
We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in github/awesome-copilot, which our catalogue first saw on October 7, 2026.
Geofeed Tuner next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Geofeed Tuner this skillgithub/awesome-copilot | 40k | 1 repos | ~12k | Automated safety check: Pass | Apache-2.0 | |
| Instrument Data To Allotropeaws-samples/amazon-bedrock-agents-healthcare-lifesciences | 274 | 2 repos | ~2.7k | Automated safety check: Pass | Apache-2.0 | |
| Sector Analysttradermonty/claude-trading-skills | 3k | 1 repos | ~2.3k | Automated safety check: Pass | MIT | |
| Cliare Artifact Reviewmodiqo/cliare | 469 | — | ~2.4k | Automated safety check: Pass | Apache-2.0 | |
| Convert Fileduckdb/duckdb-skills | 599 | 1 repos | ~720 | Automated safety check: Notes | MIT | |
| Research Integrity Auditxuzhougeng/wisp-science | 1k | — | ~2.6k | Automated safety check: Pass | AGPL-3.0 |
aws-samples/amazon-bedrock-agents-healthcare-lifesciences
Convert laboratory instrument output files (PDF, CSV, Excel, TXT) to Allotrope Simple Model (ASM) JSON format or flattened 2D CSV.
tradermonty/claude-trading-skills
This skill should be used when analyzing sector rotation patterns and market cycle positioning.
modiqo/cliare
A skill your agent uses when reviewing a CLIARE measurement artifact directory, explaining score changes, triaging issues, finding evidence, or proposing CLI remediation work from artifact-map.json…
duckdb/duckdb-skills
Convert any data file to another format: CSV, Parquet, JSON, Excel, GeoJSON, and more.
xuzhougeng/wisp-science
学术审查 / research-integrity screening of a manuscript's figures and reported numbers.
LinklyAI/best-skills
Daily cross-platform rankings of AI agent skills (skills.sh, ClawHub, Tencent SkillHub, GitHub, X/HN/Bluesky).
github/awesome-copilot
Maps an unfamiliar codebase into seven evidence-backed documents in docs/codebase/, using a scan script and templates, for onboarding or architecture write-ups.
github/awesome-copilot
Designs Azure infrastructure from a natural-language description, or diagrams an existing resource group, then refines the design through conversation and deploys it with Bicep.
github/awesome-copilot
Generates, edits and validates draw.io files with correct mxGraph XML, covering flowcharts, architecture, sequence, ER and UML class diagrams.
github/awesome-copilot
Cleans raw credit data and screens variables before loan modeling, dropping unstable, noisy or redundant features and writing an Excel report of every step.
github/awesome-copilot
Builds a warm, browser-based daily focus board the user updates by talking to their agent, with Eisenhower priorities, a brain-dump box and kind not-today carryover.
github/awesome-copilot
End-to-end skill for building, testing, linting, versioning, and publishing a production-grade Python library to PyPI.
Categories
A skill your agent uses whenever the user mentions IP geolocation feeds, RFC 8805, geofeeds, or wants help creating, tuning, validating, or publishing a self-published IP geolocation feed in CSV…. Geofeed Tuner is an agent skill from github/awesome-copilot, published by the product's own GitHub organization. Use this skill whenever the user mentions IP geolocation feeds, RFC 8805, geofeeds, or wants help creating, tuning, validating, or publishing a self-published IP geolocation feed in CSV format.
Geofeed Tuner fits situations like: the user mentions IP geolocation feeds; wants help creating; publishing a self-published IP geolocation feed in CSV format; internal IP address management — applies only to publicly routable IP addresses.
Run `npx skills add github/awesome-copilot --skill geofeed-tuner -a claude-code`. Or copy the skill folder (skills/geofeed-tuner in github/awesome-copilot) into .claude/skills/geofeed-tuner in your project. Claude Code loads it when a task matches its description.
Run `npx skills add github/awesome-copilot --skill geofeed-tuner -a codex`. Or copy the skill folder (skills/geofeed-tuner in github/awesome-copilot) into .agents/skills/geofeed-tuner in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add github/awesome-copilot --skill geofeed-tuner -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/geofeed-tuner, .gemini/skills/geofeed-tuner, .github/skills/geofeed-tuner and .opencode/skills/geofeed-tuner in your project.
SKILL.md names no scripts, command-line tools or credentials: Geofeed Tuner is instructions for the agent only. Our summary lists: Python 3. Compatibility (from SKILL.md): Requires Python 3.
SKILL.md names 1 domain. In commands or code: mcp.fastah.ai; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
Geofeed Tuner is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 12k tokens (SKILL.md is roughly 49k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 8.5k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Geofeed Tuner: Instrument Data To Allotrope (aws-samples/amazon-bedrock-agents-healthcare-lifesciences, 274 stars), Sector Analyst (tradermonty/claude-trading-skills, 3k stars), Cliare Artifact Review (modiqo/cliare, 469 stars) and Convert File (duckdb/duckdb-skills, 599 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
github (a GitHub organization, an official publisher) maintains it in github/awesome-copilot, which has 39,748 GitHub stars. The repository holds 417 skills in this directory. The repository was last updated on October 7, 2026.
Source: github/awesome-copilot on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.